Commit 5ba2132f authored by Chris Scott's avatar Chris Scott

Implement stationary-heartbeat location monitor in order to keep...

Implement stationary-heartbeat location monitor in order to keep proximity-detection alive.  Android 4.4 proximity detectors are supposed to fire after 4min while suspended but I don'tt see that happening.  This Alarm of fixed time to ping the GPS at regular interval seems to make stationary-exit work well.  Also turned on CellLocationChange event-listener but that only works in foreground unfortunately.  IF that actually worked, we could have something similar to ios Significant Changes system.  This is why we have to poll the GPS regularly while in stationary-mode
parent 1d72a53d
...@@ -11,6 +11,7 @@ import org.json.JSONObject; ...@@ -11,6 +11,7 @@ import org.json.JSONObject;
import com.tenforwardconsulting.cordova.bgloc.data.DAOFactory; import com.tenforwardconsulting.cordova.bgloc.data.DAOFactory;
import com.tenforwardconsulting.cordova.bgloc.data.LocationDAO; import com.tenforwardconsulting.cordova.bgloc.data.LocationDAO;
import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.media.AudioManager; import android.media.AudioManager;
...@@ -19,6 +20,8 @@ import android.telephony.PhoneStateListener; ...@@ -19,6 +20,8 @@ import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.telephony.CellLocation; import android.telephony.CellLocation;
import android.app.NotificationManager;
import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
...@@ -50,6 +53,7 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -50,6 +53,7 @@ public class LocationUpdateService extends Service implements LocationListener {
private static final String STATIONARY_REGION_ACTION = "com.tenforwardconsulting.cordova.bgloc.STATIONARY_REGION_ACTION"; private static final String STATIONARY_REGION_ACTION = "com.tenforwardconsulting.cordova.bgloc.STATIONARY_REGION_ACTION";
private static final String STATIONARY_ALARM_ACTION = "com.tenforwardconsulting.cordova.bgloc.STATIONARY_ALARM_ACTION"; private static final String STATIONARY_ALARM_ACTION = "com.tenforwardconsulting.cordova.bgloc.STATIONARY_ALARM_ACTION";
private static final String SINGLE_LOCATION_UPDATE_ACTION = "com.tenforwardconsulting.cordova.bgloc.SINGLE_LOCATION_UPDATE_ACTION"; private static final String SINGLE_LOCATION_UPDATE_ACTION = "com.tenforwardconsulting.cordova.bgloc.SINGLE_LOCATION_UPDATE_ACTION";
private static final String STATIONARY_LOCATION_MONITOR_ACTION = "com.tenforwardconsulting.cordova.bgloc.STATIONARY_LOCATION_MONITOR_ACTION";
private static long STATIONARY_TIMEOUT = 60 * 1000 * 2; private static long STATIONARY_TIMEOUT = 60 * 1000 * 2;
private static final Integer MAX_STATIONARY_ACQUISITION_ATTEMPTS = 3; private static final Integer MAX_STATIONARY_ACQUISITION_ATTEMPTS = 3;
private static final Integer MAX_SPEED_ACQUISITION_ATTEMPTS = 3; private static final Integer MAX_SPEED_ACQUISITION_ATTEMPTS = 3;
...@@ -64,6 +68,7 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -64,6 +68,7 @@ public class LocationUpdateService extends Service implements LocationListener {
private float stationaryRadius; private float stationaryRadius;
private Location stationaryLocation; private Location stationaryLocation;
private PendingIntent stationaryAlarmPI; private PendingIntent stationaryAlarmPI;
private PendingIntent stationaryLocationMonitorPI;
private PendingIntent stationaryRegionPI; private PendingIntent stationaryRegionPI;
private PendingIntent singleUpdatePI; private PendingIntent singleUpdatePI;
...@@ -86,6 +91,7 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -86,6 +91,7 @@ public class LocationUpdateService extends Service implements LocationListener {
private LocationManager locationManager; private LocationManager locationManager;
private AlarmManager alarmManager; private AlarmManager alarmManager;
private ConnectivityManager connectivityManager; private ConnectivityManager connectivityManager;
private NotificationManager notificationManager;
public static TelephonyManager telephonyManager = null; public static TelephonyManager telephonyManager = null;
@Override @Override
...@@ -104,6 +110,8 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -104,6 +110,8 @@ public class LocationUpdateService extends Service implements LocationListener {
alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
toneGenerator = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100); toneGenerator = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
notificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// Stop-detection PI // Stop-detection PI
stationaryAlarmPI = PendingIntent.getBroadcast(this, 0, new Intent(STATIONARY_ALARM_ACTION), 0); stationaryAlarmPI = PendingIntent.getBroadcast(this, 0, new Intent(STATIONARY_ALARM_ACTION), 0);
...@@ -113,10 +121,17 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -113,10 +121,17 @@ public class LocationUpdateService extends Service implements LocationListener {
stationaryRegionPI = PendingIntent.getBroadcast(this, 0, new Intent(STATIONARY_REGION_ACTION), PendingIntent.FLAG_CANCEL_CURRENT); stationaryRegionPI = PendingIntent.getBroadcast(this, 0, new Intent(STATIONARY_REGION_ACTION), PendingIntent.FLAG_CANCEL_CURRENT);
registerReceiver(stationaryRegionReceiver, new IntentFilter(STATIONARY_REGION_ACTION)); registerReceiver(stationaryRegionReceiver, new IntentFilter(STATIONARY_REGION_ACTION));
// Stationary location monitor PI
stationaryLocationMonitorPI = PendingIntent.getBroadcast(this, 0, new Intent(STATIONARY_LOCATION_MONITOR_ACTION), 0);
registerReceiver(stationaryLocationMonitorReceiver, new IntentFilter(STATIONARY_LOCATION_MONITOR_ACTION));
// One-shot PI (TODO currently unused) // One-shot PI (TODO currently unused)
singleUpdatePI = PendingIntent.getBroadcast(this, 0, new Intent(SINGLE_LOCATION_UPDATE_ACTION), PendingIntent.FLAG_UPDATE_CURRENT); singleUpdatePI = PendingIntent.getBroadcast(this, 0, new Intent(SINGLE_LOCATION_UPDATE_ACTION), PendingIntent.FLAG_CANCEL_CURRENT);
registerReceiver(singleUpdateReceiver, new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION)); registerReceiver(singleUpdateReceiver, new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION));
// Listen to Cell-tower switches (NOTE does not operate while suspended)
telephonyManager.listen(phoneStateListener, LISTEN_CELL_LOCATION);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
wakeLock.acquire(); wakeLock.acquire();
...@@ -141,6 +156,22 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -141,6 +156,22 @@ public class LocationUpdateService extends Service implements LocationListener {
desiredAccuracy = Integer.parseInt(intent.getStringExtra("desiredAccuracy")); desiredAccuracy = Integer.parseInt(intent.getStringExtra("desiredAccuracy"));
locationTimeout = Integer.parseInt(intent.getStringExtra("locationTimeout")); locationTimeout = Integer.parseInt(intent.getStringExtra("locationTimeout"));
isDebugging = Boolean.parseBoolean(intent.getStringExtra("isDebugging")); isDebugging = Boolean.parseBoolean(intent.getStringExtra("isDebugging"));
// Build a Notification required for running service in foreground.
Intent main = new Intent(this, BackgroundGpsPlugin.class);
main.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, main, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this)
.setContentTitle("Tracking ON")
.setContentText("Content Text")
.setSmallIcon(android.R.drawable.ic_menu_mylocation)
.setContentIntent(pendingIntent)
.build();
notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_NO_CLEAR;
startForeground(startId, notification);
} }
Log.i(TAG, "- url: " + url); Log.i(TAG, "- url: " + url);
Log.i(TAG, "- token: " + authToken); Log.i(TAG, "- token: " + authToken);
...@@ -151,13 +182,6 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -151,13 +182,6 @@ public class LocationUpdateService extends Service implements LocationListener {
Log.i(TAG, "- isDebugging: " + isDebugging); Log.i(TAG, "- isDebugging: " + isDebugging);
this.setPace(false); this.setPace(false);
/**
* Experimental cell-location-change handler. Hoping to implement something like ios Significant Changes system
*
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(phoneStateListener, LISTEN_CELL_LOCATION);
*
*/
//We want this service to continue running until it is explicitly stopped //We want this service to continue running until it is explicitly stopped
return START_REDELIVER_INTENT; return START_REDELIVER_INTENT;
...@@ -200,6 +224,7 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -200,6 +224,7 @@ public class LocationUpdateService extends Service implements LocationListener {
isAcquiringStationaryLocation = true; isAcquiringStationaryLocation = true;
stationaryLocationAttempts = 0; stationaryLocationAttempts = 0;
} }
// Temporarily turn on super-aggressive geolocation on all providers when acquiring velocity or stationary location. // Temporarily turn on super-aggressive geolocation on all providers when acquiring velocity or stationary location.
if (isAcquiringSpeed || isAcquiringStationaryLocation) { if (isAcquiringSpeed || isAcquiringStationaryLocation) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
...@@ -244,7 +269,7 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -244,7 +269,7 @@ public class LocationUpdateService extends Service implements LocationListener {
* @return The most accurate and / or timely previously detected location. * @return The most accurate and / or timely previously detected location.
*/ */
public Location getLastBestLocation(int minDistance, long minTime) { public Location getLastBestLocation(int minDistance, long minTime) {
Log.i(TAG, "- fetching last best location"); Log.i(TAG, "- fetching last best location " + minDistance + "," + minTime);
Location bestResult = null; Location bestResult = null;
float bestAccuracy = Float.MAX_VALUE; float bestAccuracy = Float.MAX_VALUE;
long bestTime = Long.MIN_VALUE; long bestTime = Long.MIN_VALUE;
...@@ -254,11 +279,13 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -254,11 +279,13 @@ public class LocationUpdateService extends Service implements LocationListener {
// If no result is found within maxTime, return the newest Location. // If no result is found within maxTime, return the newest Location.
List<String> matchingProviders = locationManager.getAllProviders(); List<String> matchingProviders = locationManager.getAllProviders();
for (String provider: matchingProviders) { for (String provider: matchingProviders) {
Log.d(TAG, "- provider: " + provider);
Location location = locationManager.getLastKnownLocation(provider); Location location = locationManager.getLastKnownLocation(provider);
Log.d(TAG, location.toString());
if (location != null) { if (location != null) {
float accuracy = location.getAccuracy(); float accuracy = location.getAccuracy();
long time = location.getTime(); long time = location.getTime();
Log.d(TAG, "time>minTime: " + (time > minTime) + ", accuracy<bestAccuracy: " + (accuracy < bestAccuracy));
if ((time > minTime && accuracy < bestAccuracy)) { if ((time > minTime && accuracy < bestAccuracy)) {
bestResult = location; bestResult = location;
bestAccuracy = accuracy; bestAccuracy = accuracy;
...@@ -326,6 +353,8 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -326,6 +353,8 @@ public class LocationUpdateService extends Service implements LocationListener {
scaledDistanceFilter = newDistanceFilter; scaledDistanceFilter = newDistanceFilter;
setPace(true); setPace(true);
} }
} else if (stationaryLocation != null) {
return;
} }
// Go ahead and cache, push to server // Go ahead and cache, push to server
lastLocation = location; lastLocation = location;
...@@ -367,9 +396,15 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -367,9 +396,15 @@ public class LocationUpdateService extends Service implements LocationListener {
location.getLatitude(), location.getLatitude(),
location.getLongitude(), location.getLongitude(),
(location.getAccuracy() < stationaryRadius) ? stationaryRadius : location.getAccuracy(), (location.getAccuracy() < stationaryRadius) ? stationaryRadius : location.getAccuracy(),
-1, (long)-1,
stationaryRegionPI stationaryRegionPI
); );
// proximity-alerts don't seem to work while suspended in latest Android 4.42 (works in 4.03). Have to use AlarmManager to sample
// location at regular intervals with a one-shot.
long interval = (long) locationTimeout * 1000;
long start = System.currentTimeMillis() + interval;
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, start, interval, stationaryLocationMonitorPI);
} }
/** /**
...@@ -379,8 +414,9 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -379,8 +414,9 @@ public class LocationUpdateService extends Service implements LocationListener {
if (isDebugging) { if (isDebugging) {
new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100).startTone(ToneGenerator.TONE_CDMA_CONFIRM); new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100).startTone(ToneGenerator.TONE_CDMA_CONFIRM);
} }
// There MUST be a valid, recent location if this event-handler was called. // There MUST be a valid, recent location if this event-handler was called.
Location location = getLastBestLocation((int) stationaryRadius, locationTimeout * 1000); Location location = getLastBestLocation((int) stationaryRadius, System.currentTimeMillis()-(locationTimeout * 1000));
if (location != null) { if (location != null) {
// Filter-out spurious region-exits: must have at least a little speed to move out of stationary-region. // Filter-out spurious region-exits: must have at least a little speed to move out of stationary-region.
...@@ -390,8 +426,13 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -390,8 +426,13 @@ public class LocationUpdateService extends Service implements LocationListener {
} else { } else {
Log.i(TAG, "- exit stationary region receiver was triggered but could not fetch the last-best location!"); Log.i(TAG, "- exit stationary region receiver was triggered but could not fetch the last-best location!");
} }
// Cancel the periodic stationary location monitor alarm.
alarmManager.cancel(stationaryLocationMonitorPI);
// Kill the current region-monitor we just walked out of. // Kill the current region-monitor we just walked out of.
locationManager.removeProximityAlert(stationaryRegionPI); locationManager.removeProximityAlert(stationaryRegionPI);
// Engage aggressive tracking. // Engage aggressive tracking.
this.setPace(true); this.setPace(true);
} }
...@@ -399,26 +440,33 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -399,26 +440,33 @@ public class LocationUpdateService extends Service implements LocationListener {
/** /**
* TODO Experimental cell-tower change system; something like ios significant changes. * TODO Experimental cell-tower change system; something like ios significant changes.
*/ */
public void onCellLocationChanged(CellLocation cellLocation) { public void onCellLocationChange(CellLocation cellLocation) {
Log.i(TAG, "- onCellLocationChanged"); Log.i(TAG, "- onCellLocationChange" + cellLocation.toString());
Location location = getLastBestLocation((int) stationaryRadius, locationTimeout * 1000); if (isDebugging) {
if (location != null) { Toast.makeText(this, "Cellular location change", Toast.LENGTH_LONG).show();
Log.i(TAG, "location: " + location.getLatitude() + "," + location.getLongitude() + ", accuracy: " + location.getAccuracy()); // @sound High-pitch beep-beep-beep
toneGenerator.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD);
}
if (!isMoving && stationaryLocation != null) {
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
criteria.setPowerRequirement(Criteria.POWER_HIGH);
locationManager.requestSingleUpdate(criteria, singleUpdatePI);
} }
} }
/** /**
* Broadcast receiver for receiving a single-update from LocationManager. * Broadcast receiver for receiving a single-update from LocationManager.
* UN-USED
*/ */
private BroadcastReceiver singleUpdateReceiver = new BroadcastReceiver() { private BroadcastReceiver singleUpdateReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
unregisterReceiver(singleUpdateReceiver);
String key = LocationManager.KEY_LOCATION_CHANGED; String key = LocationManager.KEY_LOCATION_CHANGED;
Location location = (Location)intent.getExtras().get(key); Location location = (Location)intent.getExtras().get(key);
if (location != null) if (location != null) {
Log.d(TAG, "- singleUpdateReciever" + location.toString());
onLocationChanged(location); onLocationChanged(location);
}
locationManager.removeUpdates(singleUpdatePI); locationManager.removeUpdates(singleUpdatePI);
} }
}; };
...@@ -430,11 +478,26 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -430,11 +478,26 @@ public class LocationUpdateService extends Service implements LocationListener {
@Override @Override
public void onReceive(Context context, Intent intent) public void onReceive(Context context, Intent intent)
{ {
// Put here YOUR code.
Log.i(TAG, "- stationaryAlarm fired"); Log.i(TAG, "- stationaryAlarm fired");
setPace(false); setPace(false);
} }
}; };
/**
* Broadcast receiver to handle stationaryMonitor alarm, fired at low frequency while monitoring stationary-region.
* This is required because latest Android proximity-alerts don't seem to operate while suspended. Regularly polling
* the location seems to trigger the proximity-alerts while suspended.
*/
private BroadcastReceiver stationaryLocationMonitorReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent)
{
Log.i(TAG, "- stationaryLocationMonitorReceiver fired");
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
criteria.setPowerRequirement(Criteria.POWER_HIGH);
locationManager.requestSingleUpdate(criteria, singleUpdatePI);
}
};
/** /**
* Broadcast receiver which detects a user has exit his circular stationary-region determined by the greater of stationaryLocation.getAccuracy() OR stationaryRadius * Broadcast receiver which detects a user has exit his circular stationary-region determined by the greater of stationaryLocation.getAccuracy() OR stationaryRadius
*/ */
...@@ -464,7 +527,7 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -464,7 +527,7 @@ public class LocationUpdateService extends Service implements LocationListener {
@Override @Override
public void onCellLocationChanged(CellLocation location) public void onCellLocationChanged(CellLocation location)
{ {
onCellLocationChanged(location); onCellLocationChange(location);
} }
}; };
...@@ -561,9 +624,11 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -561,9 +624,11 @@ public class LocationUpdateService extends Service implements LocationListener {
private void cleanUp() { private void cleanUp() {
locationManager.removeUpdates(this); locationManager.removeUpdates(this);
alarmManager.cancel(stationaryAlarmPI); alarmManager.cancel(stationaryAlarmPI);
unregisterReceiver(stationaryAlarmReceiver); unregisterReceiver(stationaryAlarmReceiver);
unregisterReceiver(stationaryRegionReceiver);
unregisterReceiver(singleUpdateReceiver); unregisterReceiver(singleUpdateReceiver);
unregisterReceiver(stationaryRegionReceiver);
unregisterReceiver(stationaryLocationMonitorReceiver);
if (stationaryLocation != null && !isMoving) { if (stationaryLocation != null && !isMoving) {
try { try {
...@@ -572,7 +637,7 @@ public class LocationUpdateService extends Service implements LocationListener { ...@@ -572,7 +637,7 @@ public class LocationUpdateService extends Service implements LocationListener {
Log.w(TAG, "- Something bad happened while removing proximity-alert"); Log.w(TAG, "- Something bad happened while removing proximity-alert");
} }
} }
stopForeground(true);
wakeLock.release(); wakeLock.release();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment