Java tutorial
// -*- mode: java; c-basic-offset: 2; -*- // Copyright 2009-2011 Google, All Rights reserved // Copyright 2011-2012 MIT, All rights reserved // Released under the MIT License https://raw.github.com/mit-cml/app-inventor/master/mitlicense.txt package com.google.appinventor.components.runtime; import java.util.ArrayList; import java.util.List; import android.os.Handler; import android.os.Message; import android.util.Log; 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 com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import edu.mit.media.funf.FunfManager; import edu.mit.media.funf.json.IJsonObject; import edu.mit.media.funf.probe.Probe.DataListener; import edu.mit.media.funf.probe.builtin.LocationProbe; import edu.mit.media.funf.probe.builtin.ProbeKeys.LocationKeys; import edu.mit.media.funf.probe.builtin.SensorProbe; import edu.mit.media.funf.probe.builtin.SimpleLocationProbe; /** * Record GPS location periodically * * @author fuming@mit.edu (Fuming Shih) * */ @DesignerComponent(version = YaVersion.LOCATIONPROBESENSOR_COMPONENT_VERSION, description = "<p>Similar compoment to LocationSensor that provides location information, " + "including longitude, latitude, altitude (if supported by the device). </p>" + "The \"probe\" can be scheduled to periodically receive location information. " + "In addition, LocationProbeSensor filters the verbose location set for the most accurate " + "location within a max wait time (default 2 mins), ending early if it finds a location that has at most the goodEnoughAccuracy." + "The default max_wait_time is 2 mins and the default goodEnoughAccuracy is 80. " + "Useful for sparse polling of location to limit battery usage.", category = ComponentCategory.SENSORS, nonVisible = true, iconName = "images/locationProbe.png") @SimpleObject @UsesPermissions(permissionNames = "android.permission.ACCESS_COARSE_LOCATION, " + "android.permission.ACCESS_FINE_LOCATION") @UsesLibraries(libraries = "funf.jar") public class LocationProbeSensor extends ProbeBase { private final String TAG = "LocationProbeSensor"; protected final String SIMPLE_LOCATION_PROBE = "edu.mit.media.funf.probe.builtin.SimpleLocationProbe"; public static final int UNKNOWN_VALUE = 0; // Location info private double mLatitude = UNKNOWN_VALUE; private double mLongitude = UNKNOWN_VALUE; private float mAccuracy = UNKNOWN_VALUE; private long timestamp; private String mProvider = ""; 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 GOOD_ENOUGHT_ACCURACY = 80; private boolean useGPS = true; private boolean useNetwork = true; private boolean useCache = false; private int goodEnoughAccurary; public LocationProbeSensor(ComponentContainer container) { super(container); // TODO Auto-generated constructor stub // Set up listeners form.registerForOnDestroy(this); mainUIThreadActivity = container.$context(); Log.i(TAG, "Before create probe"); gson = new GsonBuilder().registerTypeAdapterFactory(FunfManager.getProbeFactory(mainUIThreadActivity)) .create(); // JsonObject config = new JsonObject(); // // probe = gson.fromJson(config, SimpleLocationProbe.class); interval = SCHEDULE_INTERVAL; duration = SCHEDULE_DURATION; goodEnoughAccurary = GOOD_ENOUGHT_ACCURACY; } final Handler myHandler = new Handler() { @Override public void handleMessage(Message msg) { IJsonObject data = (IJsonObject) msg.obj; Log.i(TAG, "Update component's varibles....."); mLatitude = data.get(LocationKeys.LATITUDE).getAsDouble(); mLongitude = data.get(LocationKeys.LONGITUDE).getAsDouble(); mAccuracy = data.get(LocationKeys.ACCURACY).getAsFloat(); timestamp = data.get(LocationKeys.TIMESTAMP).getAsLong(); mProvider = data.get("mProvider").getAsString(); 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. */ @SimpleEvent public void LocationInfoReceived(final long timestamp, final double mLatitude, final double mLongitude, final float mAccuracy, final String mProvider) { if (enabled || enabledSchedule) { mainUIThreadActivity.runOnUiThread(new Runnable() { public void run() { Log.i(TAG, "LocationInfoReceived() is called"); EventDispatcher.dispatchEvent(LocationProbeSensor.this, "LocationInfoReceived", timestamp, mLatitude, mLongitude, mAccuracy, mProvider); } }); } } /** * Indicates that the updating Location info has completed. */ @SimpleEvent public void LocationUpdateComplete() { if (enabled || enabledSchedule) { mainUIThreadActivity.runOnUiThread(new Runnable() { public void run() { Log.i(TAG, "LocationUpdateComplete() is called"); EventDispatcher.dispatchEvent(LocationProbeSensor.this, "LocationUpdateComplete"); } }); } } private DataListener listener = new DataListener() { @Override public void onDataCompleted(IJsonObject completeProbeUri, JsonElement arg1) { LocationUpdateComplete(); } @Override public void onDataReceived(IJsonObject completeProbeUri, IJsonObject data) { Log.i(TAG, "receive data"); /* returned json format * */ Log.i(TAG, "mLatitude:" + data.get(LocationKeys.LATITUDE).getAsDouble()); Log.i(TAG, "mLongitude:" + data.get(LocationKeys.LONGITUDE).getAsDouble()); Log.i(TAG, "mAccuracy:" + data.get(LocationKeys.ACCURACY).getAsFloat()); Log.i(TAG, "mProvider:" + data.get("mProvider").getAsString()); Log.i(TAG, "timestamp:" + data.get(LocationKeys.TIMESTAMP).getAsLong()); //save data to DB is enabledSaveToDB is true if (enabledSaveToDB) { saveToDB(completeProbeUri, data); } Message msg = myHandler.obtainMessage(); msg.obj = data; myHandler.sendMessage(msg); } }; /** * Indicates whether the sensor should "run once" to listen for location information * and raise the corresponding events. */ @SimpleFunction(description = "Enable location probe to run once and receive location") @Override public void Enabled(boolean enabled) { // TODO Auto-generated method stub if (this.enabled != enabled) this.enabled = enabled; if (enabled) { //recreate json config JsonObject config = createNewConfig(this.useGPS, this.useNetwork, this.goodEnoughAccurary); probe = gson.fromJson(config, SimpleLocationProbe.class); probe.registerListener(listener); Log.i(TAG, "register location listener for run-once"); Log.i(TAG, "run-once config:" + config); } else { probe.unregisterListener(listener); Log.i(TAG, "unregister location run-once listener"); } } /* * recreate json config */ private JsonObject createNewConfig(boolean useGPS, boolean useNetwork, int goodEnoughAccurary) { JsonObject config = new JsonObject(); config.addProperty("goodEnoughAccuracy", goodEnoughAccurary); config.addProperty("useGPS", useGPS); config.addProperty("useNetwork", useNetwork); config.addProperty("useCache", useCache); return config; } /** * Set whether the location info will use the last known location without acquiring new location from * GPS or Network fix * @param newVal */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "Fsocalse") @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) { if (useCache != newVal) { this.useCache = newVal; } } @SimpleProperty(category = PropertyCategory.BEHAVIOR) public boolean UseCache() { return useCache; } /** * Set the good-enough accuracy for location sensor */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_INTEGER, defaultValue = "80") @SimpleProperty public void GoodEnoughAccuracy(int newVal) { if (goodEnoughAccurary != newVal) { this.goodEnoughAccurary = newVal; } } /** * The goodEnoughAccuracy of the location data that the sensor listens to */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public int GoodEnoughAccuracy() { return goodEnoughAccurary; } /** * Set whether to use GPS or not for the location provider */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "True") @SimpleProperty public void UseGPS(boolean newVal) { if (useGPS != newVal) { this.useGPS = newVal; } } /** * Indicate whether the locationProbe uses GPS or not */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) public boolean UseGPS() { return useGPS; } /** * Set whether to use Network or not for the location provider */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "True") @SimpleProperty public void UseNetwork(boolean newVal) { if (useNetwork != newVal) { this.useNetwork = newVal; } } /** * Indicate whether the locationProbe uses GPS or not */ @SimpleProperty(category = PropertyCategory.BEHAVIOR) 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; dataRequest = getDataRequest(interval, duration, SIMPLE_LOCATION_PROBE); ((JsonObject) dataRequest).addProperty("goodEnoughAccurary", goodEnoughAccurary); ((JsonObject) dataRequest).addProperty("useGPS", useGPS); ((JsonObject) dataRequest).addProperty("useNetwork", useNetwork); ((JsonObject) dataRequest).addProperty("useCache", useCache); 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."); } // /** // * The most recent available accuracy value. If no value is available, // * 0 will be returned. // */ // @SimpleProperty(category = PropertyCategory.BEHAVIOR) // public double Accuracy() { // return mAccuracy; // } // // /** // * The most recent available longitude value. If no value is available, // * 0 will be returned. // */ // @SimpleProperty(category = PropertyCategory.BEHAVIOR) // public double Longitude() { // return mLongitude; // } // // /** // * The most recently available latitude value. If no value is available, // * 0 will be returned. // */ // @SimpleProperty(category = PropertyCategory.BEHAVIOR) // public double Latitude() { // return mLatitude; // } // // /** // * The type of the provider. If no value is available, // * "" will be returned. // */ // @SimpleProperty(category = PropertyCategory.BEHAVIOR) // public String Provider() { // return mProvider; // } // // /** // * Returns the timestamp of latest reading // */ // @SimpleProperty(description = "The timestamp of this sensor event.") // public float Timestamp() { // Log.i(TAG, "returning timestamp: " + timestamp); // return timestamp; // } /* * 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; } /* * 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; } }