forked from jens/Automation
825 lines
27 KiB
Java
825 lines
27 KiB
Java
|
package com.jens.automation2;
|
||
|
|
||
|
import android.content.Context;
|
||
|
import android.location.Criteria;
|
||
|
import android.location.Location;
|
||
|
import android.location.LocationListener;
|
||
|
import android.location.LocationManager;
|
||
|
import android.os.Bundle;
|
||
|
import android.os.Handler;
|
||
|
import android.os.Message;
|
||
|
import android.util.Log;
|
||
|
import android.widget.Toast;
|
||
|
|
||
|
import com.jens.automation2.Trigger.Trigger_Enum;
|
||
|
|
||
|
import java.io.FileNotFoundException;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Collections;
|
||
|
|
||
|
public class PointOfInterest implements Comparable<PointOfInterest>
|
||
|
{
|
||
|
// The array containing all POIs
|
||
|
private static ArrayList<PointOfInterest> pointOfInterestCollection = new ArrayList<PointOfInterest>();
|
||
|
|
||
|
public static ArrayList<PointOfInterest> getPointOfInterestCollection()
|
||
|
{
|
||
|
Collections.sort(pointOfInterestCollection);
|
||
|
return pointOfInterestCollection;
|
||
|
}
|
||
|
|
||
|
public static void setPointOfInterestCollection(ArrayList<PointOfInterest> pointOfInterestCollection)
|
||
|
{
|
||
|
Collections.sort(pointOfInterestCollection);
|
||
|
PointOfInterest.pointOfInterestCollection = pointOfInterestCollection;
|
||
|
}
|
||
|
|
||
|
// name and location
|
||
|
private String name;
|
||
|
private Location location;
|
||
|
private double radius;
|
||
|
|
||
|
private String oldName;
|
||
|
private boolean activated=false;
|
||
|
|
||
|
private static Location[] locationRingBuffer = new Location[Settings.locationRingBufferSize];
|
||
|
private static int locationRingBufferLastPosition = -1;
|
||
|
|
||
|
private static boolean gpsLocationListenerArmed = false;
|
||
|
private static LocationManager gpsComparisonLocationManager;
|
||
|
private static GpsComparisonLocationListener gpsComparisonLocationListener;
|
||
|
private static TimeoutHandler timeoutHandler = new TimeoutHandler();
|
||
|
private static boolean timeoutHandlerActive = false;
|
||
|
public String getName()
|
||
|
{
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
public static void stopRoutine()
|
||
|
{
|
||
|
if(gpsLocationListenerArmed)
|
||
|
stopGpsMeasurement();
|
||
|
}
|
||
|
|
||
|
public void setName(String desiredName)
|
||
|
{
|
||
|
this.oldName = this.name;
|
||
|
this.name = desiredName;
|
||
|
}
|
||
|
|
||
|
public Location getLocation()
|
||
|
{
|
||
|
return location;
|
||
|
}
|
||
|
|
||
|
public void setLocation(Location location)
|
||
|
{
|
||
|
this.location = location;
|
||
|
}
|
||
|
|
||
|
public double getRadius()
|
||
|
{
|
||
|
return radius;
|
||
|
}
|
||
|
|
||
|
public void setRadius(double radius, Context context) throws Exception
|
||
|
{
|
||
|
if(radius <= 0)
|
||
|
throw new Exception(context.getResources().getString(R.string.radiusHasToBePositive));
|
||
|
|
||
|
this.radius = radius;
|
||
|
}
|
||
|
|
||
|
public void setActivated(boolean value)
|
||
|
{
|
||
|
this.activated = value;
|
||
|
}
|
||
|
public boolean isActivated()
|
||
|
{
|
||
|
return activated;
|
||
|
}
|
||
|
|
||
|
public static void positionUpdate(Location newLocation, AutomationService parentService, boolean forceApply, boolean skipVerfication)
|
||
|
{
|
||
|
// StackTraceElement[] trace = Thread.currentThread().getStackTrace();
|
||
|
// for(StackTraceElement element : trace)
|
||
|
// {
|
||
|
// Log.i("Trace", Arrays.toString(trace));
|
||
|
// }
|
||
|
|
||
|
// Assumption "active POI = closest POI" is wrong!
|
||
|
|
||
|
if(newLocation != null)
|
||
|
{
|
||
|
String accuracyString = "n./a.";
|
||
|
if(newLocation.hasAccuracy())
|
||
|
accuracyString = String.valueOf(newLocation.getAccuracy() + " m");
|
||
|
Miscellaneous.logEvent("i", "POI", "Got position update (" + String.valueOf(newLocation.getLatitude()) + " / " + String.valueOf(newLocation.getLongitude()) + " / provider: " + newLocation.getProvider() + " / Accuracy: " + accuracyString + "), checking rules.", 2);
|
||
|
|
||
|
PointOfInterest closestPoi = PointOfInterest.getClosestPOI(newLocation);
|
||
|
|
||
|
if(getActivePoi() != null)
|
||
|
Miscellaneous.logEvent("i", "POI", "Active POI: " + getActivePoi().getName() + ", distance : " + String.valueOf(newLocation.distanceTo(getActivePoi().getLocation())), 4);
|
||
|
|
||
|
if(closestPoi == null)
|
||
|
{
|
||
|
// There are no POIs defined. Not much we can do.
|
||
|
// Miscellaneous.logEvent("i", "POI", "Closest POI: n/a, distance : n/a", 4);
|
||
|
Miscellaneous.logEvent("i", "POI", "Got position update, but there are no POIs defined. Can't trigger a rule.", 3);
|
||
|
// return;
|
||
|
}
|
||
|
else
|
||
|
Miscellaneous.logEvent("i", "POI", "Closest POI: " + closestPoi.getName() + ", distance : " + String.valueOf(newLocation.distanceTo(closestPoi.getLocation())), 4);
|
||
|
|
||
|
if(
|
||
|
(getActivePoi() != null && getActivePoi().isActivated() && !getActivePoi().reachedPoiArea(newLocation))
|
||
|
|
|
||
|
(closestPoi != null && !closestPoi.isActivated() && closestPoi.reachedPoiArea(newLocation))
|
||
|
)
|
||
|
{
|
||
|
// only an active POI can be left while only a closestPOI can be entered, hence the complex if/else
|
||
|
if(getActivePoi() != null && getActivePoi().isActivated() && !getActivePoi().reachedPoiArea(newLocation))
|
||
|
Miscellaneous.logEvent("i", "POI", "May have left POI " + getActivePoi().getName() + ", checking location accuracy...", 4);
|
||
|
if(closestPoi != null && !closestPoi.isActivated() && closestPoi.reachedPoiArea(newLocation))
|
||
|
Miscellaneous.logEvent("i", "POI", "May have entered POI " + closestPoi.getName() + ", checking location accuracy...", 4);
|
||
|
|
||
|
if(forceApply)
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", parentService.getResources().getString(R.string.forcedLocationUpdate), parentService.getResources().getString(R.string.forcedLocationUpdateLong), 4);
|
||
|
|
||
|
// only an active POI can be left while only a closestPOI can be entered, hence the complex if/else
|
||
|
if(getActivePoi() != null && getActivePoi().isActivated() && !getActivePoi().reachedPoiArea(newLocation))
|
||
|
{
|
||
|
addPositionToRingBuffer(newLocation);
|
||
|
getActivePoi().deactivate(parentService);
|
||
|
}
|
||
|
if(closestPoi != null && !closestPoi.isActivated() && closestPoi.reachedPoiArea(newLocation))
|
||
|
{
|
||
|
addPositionToRingBuffer(newLocation);
|
||
|
closestPoi.activate(parentService);
|
||
|
}
|
||
|
}
|
||
|
else if(newLocation.hasAccuracy() && newLocation.getAccuracy() > Settings.satisfactoryAccuracyNetwork && !newLocation.getProvider().equals(LocationManager.GPS_PROVIDER))
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "Location update with unsatisfactory accuracy: " + String.valueOf(newLocation.getAccuracy()) + ", demanded: " + String.valueOf(Settings.satisfactoryAccuracyNetwork), 4);
|
||
|
if(!skipVerfication)
|
||
|
{
|
||
|
if(PointOfInterest.isPoiInRelevantRange(newLocation))
|
||
|
startGpsMeasurement(parentService);
|
||
|
else
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "Applying update with unsatisfactory accuracy because no defined location is in a relevant range.", 4);
|
||
|
positionUpdate(newLocation, parentService, true, false);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "Location update with unsatisfactory accuracy, but skipping verfication as requested. Effectively ignoring this update. It's probably from a passive source. Verifying it would cost battery.", 4);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "Location update with acceptable accuracy.", 4);
|
||
|
/* It may be that a previous location wasn't accurate enough, we now got a better location via network,
|
||
|
* but the GPS listener is still active and trying to find out a precise location. We need to deactivate
|
||
|
* it if we are here
|
||
|
*/
|
||
|
if(gpsLocationListenerArmed)
|
||
|
stopGpsMeasurement();
|
||
|
|
||
|
// only an active POI can be left while only a closestPOI can be entered, hence the complex if/else
|
||
|
if(getActivePoi() != null && getActivePoi().isActivated() && !getActivePoi().reachedPoiArea(newLocation))
|
||
|
{
|
||
|
addPositionToRingBuffer(newLocation);
|
||
|
getActivePoi().deactivate(parentService);
|
||
|
}
|
||
|
if(closestPoi != null && !closestPoi.isActivated() && closestPoi.reachedPoiArea(newLocation))
|
||
|
{
|
||
|
addPositionToRingBuffer(newLocation);
|
||
|
closestPoi.activate(parentService);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
Miscellaneous.logEvent("e", "POI", "Given location is null. Aborting.", 3);
|
||
|
}
|
||
|
|
||
|
public Boolean reachedPoiArea(Location currentLocation)
|
||
|
{
|
||
|
float distance = this.location.distanceTo(currentLocation);
|
||
|
|
||
|
if(distance < this.radius)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void activate(AutomationService parentService)
|
||
|
{
|
||
|
if(!this.isActivated())
|
||
|
{
|
||
|
// Deactivate all others in case nobody deactivated them
|
||
|
for(int i = 0; i < pointOfInterestCollection.size(); i++)
|
||
|
pointOfInterestCollection.get(i).deactivate(parentService);
|
||
|
|
||
|
/*
|
||
|
ConcurrentModificationErrors have been seen when using this method: for(PointOfInterest onePoi : pointOfInterestCollection)
|
||
|
|
||
|
Tue Nov 20 19:21:50 GMT+01:00 2018: e / Automation / java.util.ConcurrentModificationException
|
||
|
at java.util.ArrayList$Itr.next(ArrayList.java:860)
|
||
|
at com.jens.automation2.PointOfInterest.activate(PointOfInterest.java:227)
|
||
|
at com.jens.automation2.PointOfInterest.positionUpdate(PointOfInterest.java:199)
|
||
|
at com.jens.automation2.location.LocationProvider.setCurrentLocation(LocationProvider.java:126)
|
||
|
at com.jens.automation2.location.LocationProvider$MyPassiveLocationListener.onLocationChanged(LocationProvider.java:289)
|
||
|
at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:291)
|
||
|
at android.location.LocationManager$ListenerTransport.-wrap0(Unknown Source:0)
|
||
|
at android.location.LocationManager$ListenerTransport$1.handleMessage(LocationManager.java:236)
|
||
|
at android.os.Handler.dispatchMessage(Handler.java:105)
|
||
|
at android.os.Looper.loop(Looper.java:164)
|
||
|
at android.app.ActivityThread.main(ActivityThread.java:6944)
|
||
|
at java.lang.reflect.Method.invoke(Native Method)
|
||
|
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
|
||
|
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
|
||
|
*/
|
||
|
|
||
|
this.activated = true;
|
||
|
Settings.lastActivePoi = this;
|
||
|
Settings.writeSettings(parentService);
|
||
|
|
||
|
Miscellaneous.logEvent("i", "POI", "Reached POI " + this.getName() + ". Checking if there's a rule that applies to that.", 2);
|
||
|
|
||
|
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPoi(this, true);
|
||
|
if(ruleCandidates.size()==0)
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " not found in ANY rule.", 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " found in " + ruleCandidates.size() + " rule(s).", 2);
|
||
|
|
||
|
for(int i=0; i<ruleCandidates.size(); i++)
|
||
|
{
|
||
|
if(ruleCandidates.get(i).applies(parentService) && ruleCandidates.get(i).haveEnoughPermissions())
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "Rule " + ruleCandidates.get(i).getName() + " applies for entering POI " + this.getName() + ".", 2);
|
||
|
ruleCandidates.get(i).activate(parentService, false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
parentService.updateNotification();
|
||
|
ActivityMainScreen.updateMainScreen();
|
||
|
}
|
||
|
}
|
||
|
public void deactivate(AutomationService parentService)
|
||
|
{
|
||
|
if(this.isActivated())
|
||
|
{
|
||
|
this.activated=false; //has to stay before Rule.applies()
|
||
|
Settings.lastActivePoi = null;
|
||
|
Settings.writeSettings(parentService);
|
||
|
|
||
|
Miscellaneous.logEvent("i", "POI", "Left POI " + this.getName() + ". Checking if there's a rule that applies to that.", 2);
|
||
|
|
||
|
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesByPoi(this, false);
|
||
|
if(ruleCandidates.size()==0)
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " not found in ANY rule.", 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "POI " + this.getName() + " found in " + ruleCandidates.size() + " rule(s).", 2);
|
||
|
for(int i=0; i<ruleCandidates.size(); i++)
|
||
|
{
|
||
|
if(ruleCandidates.get(i).applies(parentService))
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "Rule " + ruleCandidates.get(i).getName() + " applies for leaving POI " + this.getName() + ".", 2);
|
||
|
ruleCandidates.get(i).activate(parentService, false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
parentService.updateNotification();
|
||
|
ActivityMainScreen.updateMainScreen();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static PointOfInterest getClosestPOI(Location currentLocation)// throws Exception
|
||
|
{
|
||
|
// return the currently closed one of all saved points of interest
|
||
|
|
||
|
if(pointOfInterestCollection.size() == 0)
|
||
|
{
|
||
|
//throw new Exception("No points of interest defined.");
|
||
|
//Toast.makeText(context, "No points of interest defined.", Toast.LENGTH_LONG).show();
|
||
|
return null;
|
||
|
}
|
||
|
else if(pointOfInterestCollection.size() == 1)
|
||
|
return pointOfInterestCollection.get(0);
|
||
|
else
|
||
|
{
|
||
|
double distance = pointOfInterestCollection.get(0).location.distanceTo(currentLocation);
|
||
|
PointOfInterest closestPoi = pointOfInterestCollection.get(0);
|
||
|
|
||
|
distance = (int) currentLocation.distanceTo(pointOfInterestCollection.get(0).location);
|
||
|
|
||
|
for(int i=1; i<pointOfInterestCollection.size(); i++)
|
||
|
{
|
||
|
if(currentLocation.distanceTo(pointOfInterestCollection.get(i).location) < distance)
|
||
|
{
|
||
|
distance = currentLocation.distanceTo(pointOfInterestCollection.get(i).location);
|
||
|
closestPoi = pointOfInterestCollection.get(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return closestPoi;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Determines if a POI is in any relevant range. Doesn't say if one is
|
||
|
* reached, but can help decide if it's worth activating GPS to be sure.
|
||
|
* @param currentLocation
|
||
|
* @return
|
||
|
*/
|
||
|
public static boolean isPoiInRelevantRange(Location currentLocation)
|
||
|
{
|
||
|
/*
|
||
|
* Radius
|
||
|
* + Precision
|
||
|
* + Self defined value
|
||
|
*/
|
||
|
|
||
|
double distance;
|
||
|
double minimumDistance;
|
||
|
|
||
|
for(PointOfInterest poi : PointOfInterest.getPointOfInterestCollection())
|
||
|
{
|
||
|
distance = poi.getLocation().distanceTo(currentLocation);
|
||
|
if(currentLocation.hasAccuracy())
|
||
|
minimumDistance = currentLocation.getAccuracy();
|
||
|
else
|
||
|
minimumDistance = 0;
|
||
|
|
||
|
minimumDistance += poi.getRadius();
|
||
|
|
||
|
if(distance < minimumDistance)
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.poiCouldBeInRange), poi.getName()), 4);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public static void loadPoisFromFile()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if(XmlFileInterface.settingsFile.exists())
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "POI", "SettingsFile " + XmlFileInterface.settingsFile.getPath() + " exists. Loading POIs from File.", 4);
|
||
|
XmlFileInterface.readFile();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Miscellaneous.logEvent("w", "POI", "SettingsFile " + XmlFileInterface.settingsFile.getPath() + " doesn't exist.", 4);
|
||
|
}
|
||
|
}
|
||
|
catch (FileNotFoundException e)
|
||
|
{
|
||
|
e.printStackTrace();
|
||
|
}
|
||
|
}
|
||
|
public static boolean writePoisToFile()
|
||
|
{
|
||
|
return XmlFileInterface.writeFile();
|
||
|
}
|
||
|
|
||
|
|
||
|
@Override
|
||
|
public String toString()
|
||
|
{
|
||
|
return this.getName();
|
||
|
}
|
||
|
public String toStringLong()
|
||
|
{
|
||
|
return this.name + ": " + String.valueOf(this.radius) + " meters around " + String.valueOf(this.location.getLatitude() + " / " + String.valueOf(this.location.getLongitude()));
|
||
|
}
|
||
|
|
||
|
public boolean create(Context context)
|
||
|
{
|
||
|
for(PointOfInterest poi : PointOfInterest.pointOfInterestCollection)
|
||
|
if(poi.getName().equals(this.getName()))
|
||
|
{
|
||
|
Toast.makeText(context, context.getResources().getString(R.string.anotherPoiByThatName), Toast.LENGTH_LONG).show();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if(plausibilityCheck())
|
||
|
{
|
||
|
PointOfInterest.pointOfInterestCollection.add(this);
|
||
|
PointOfInterest.writePoisToFile();
|
||
|
|
||
|
AutomationService service = AutomationService.getInstance();
|
||
|
if(service != null)
|
||
|
{
|
||
|
service.applySettingsAndRules();
|
||
|
//Easiest way to check for changes in location, reset the last known location.
|
||
|
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
public boolean change(Context context)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
/*
|
||
|
Check for change of rule name START
|
||
|
*/
|
||
|
if (this.oldName != null && !this.oldName.equals(this.name)) // catch oldName being null
|
||
|
{
|
||
|
//Name has changed. We need to look for rules that reference it by its name and update those references
|
||
|
|
||
|
// Check if the name is still available
|
||
|
int counter = 0; // this method should only be a temporary workaround, directly editing the referenced object may cause problems until reloading the config file
|
||
|
for (PointOfInterest poi : PointOfInterest.pointOfInterestCollection)
|
||
|
if (poi.getName().equals(this.getName()))
|
||
|
{
|
||
|
counter++;
|
||
|
}
|
||
|
if (counter > 1)
|
||
|
{
|
||
|
Toast.makeText(context, context.getResources().getString(R.string.anotherPoiByThatName), Toast.LENGTH_LONG).show();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Check if rules reference this poi
|
||
|
ArrayList<Rule> rulesThatReferenceMe = Rule.findRuleCandidatesByPoi(this);
|
||
|
if (rulesThatReferenceMe.size() > 0)
|
||
|
{
|
||
|
for (Rule oneRule : rulesThatReferenceMe)
|
||
|
{
|
||
|
for (Trigger oneTrigger : oneRule.getTriggerSet())
|
||
|
{
|
||
|
if (oneTrigger.getTriggerType() == Trigger_Enum.pointOfInterest)
|
||
|
{
|
||
|
oneTrigger.setPointOfInterest(this);
|
||
|
// We don't need to save the file. This will happen anyway in PointOfInterest.writePoisToFile() below.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
Check for change of rule name END
|
||
|
*/
|
||
|
|
||
|
if (plausibilityCheck())
|
||
|
{
|
||
|
if(PointOfInterest.writePoisToFile())
|
||
|
{
|
||
|
AutomationService service = AutomationService.getInstance();
|
||
|
if (service != null)
|
||
|
{
|
||
|
service.applySettingsAndRules();
|
||
|
//Easiest way to check for changes in location, reset the last known location.
|
||
|
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
catch(Exception e)
|
||
|
{
|
||
|
Toast.makeText(context, context.getResources().getString(R.string.unknownError), Toast.LENGTH_LONG).show();
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
public boolean delete(Context context)
|
||
|
{
|
||
|
//Check if there's a rule that contains this poi
|
||
|
ArrayList<Rule> rulesThatReferenceMe = Rule.findRuleCandidatesByPoi(this);
|
||
|
if(rulesThatReferenceMe.size() > 0)
|
||
|
{
|
||
|
String rulesString = "";
|
||
|
for(Rule rule : rulesThatReferenceMe)
|
||
|
rulesString += rule.getName() + "; ";
|
||
|
|
||
|
rulesString = rulesString.substring(0, rulesString.length()-2);
|
||
|
|
||
|
Toast.makeText(context, String.format(context.getResources().getString(R.string.poiStillReferenced), rulesString), Toast.LENGTH_LONG).show();
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PointOfInterest.pointOfInterestCollection.remove(this);
|
||
|
PointOfInterest.writePoisToFile();
|
||
|
|
||
|
AutomationService service = AutomationService.getInstance();
|
||
|
if(service != null)
|
||
|
{
|
||
|
service.applySettingsAndRules();
|
||
|
|
||
|
//Easiest way to check for changes in location, reset the last known location.
|
||
|
service.getLocationProvider().setCurrentLocation(service.getLocationProvider().getCurrentLocation(), true);
|
||
|
}
|
||
|
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static PointOfInterest getByName(String searchName) throws Exception
|
||
|
{
|
||
|
for(PointOfInterest poi : pointOfInterestCollection)
|
||
|
{
|
||
|
if(poi.name.equals(searchName))
|
||
|
return poi;
|
||
|
}
|
||
|
|
||
|
throw new Exception("PointOfInterest with name " + searchName + " not found.");
|
||
|
}
|
||
|
|
||
|
public static String[] getNamesInArray()
|
||
|
{
|
||
|
ArrayList<String> nameList = new ArrayList<String>();
|
||
|
for(PointOfInterest poi : pointOfInterestCollection)
|
||
|
{
|
||
|
nameList.add(poi.name);
|
||
|
}
|
||
|
|
||
|
return (String[])nameList.toArray(new String[pointOfInterestCollection.size()]);
|
||
|
}
|
||
|
|
||
|
public static PointOfInterest getActivePoi()
|
||
|
{
|
||
|
for(PointOfInterest poi : PointOfInterest.pointOfInterestCollection)
|
||
|
{
|
||
|
if(poi.isActivated())
|
||
|
return poi;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int compareTo(PointOfInterest another)
|
||
|
{
|
||
|
return this.getName().compareTo(another.getName());
|
||
|
}
|
||
|
|
||
|
private static boolean addPositionToRingBuffer(Location newLocation)
|
||
|
{
|
||
|
/*
|
||
|
* This method's purpose is to record the last n positions and check if they are different.
|
||
|
* In reality you will never get the exact same position twice. If you do the location engine
|
||
|
* seems to have hung up.
|
||
|
*/
|
||
|
|
||
|
try
|
||
|
{
|
||
|
if(++locationRingBufferLastPosition > locationRingBuffer.length-1)
|
||
|
locationRingBufferLastPosition = 0;
|
||
|
|
||
|
Miscellaneous.logEvent("i", "Ringbuffer.", "Adding location " + String.valueOf(newLocation.getLatitude()) + " / " + String.valueOf(newLocation.getLongitude()) + " to ringbuffer at index " + String.valueOf(locationRingBufferLastPosition), 5);
|
||
|
locationRingBuffer[locationRingBufferLastPosition] = newLocation;
|
||
|
|
||
|
/*
|
||
|
* Return values:
|
||
|
* true if the new location is different to the last one
|
||
|
* false if we get repeated values, comparing all values
|
||
|
*
|
||
|
* false indicates problems with hangups in getting locations.
|
||
|
*/
|
||
|
|
||
|
int counter = locationRingBufferLastPosition+1-1; // make a copy, not a reference
|
||
|
int previousIndex;
|
||
|
do
|
||
|
{
|
||
|
if(counter>0)
|
||
|
previousIndex = counter-1;
|
||
|
else
|
||
|
previousIndex = Settings.locationRingBufferSize-1;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
if(locationRingBuffer[counter].getLatitude() != locationRingBuffer[previousIndex].getLatitude() | locationRingBuffer[counter].getLongitude() != locationRingBuffer[previousIndex].getLongitude())
|
||
|
{
|
||
|
// If location different from last one we're fine.
|
||
|
Miscellaneous.logEvent("w", "Ringbuffer.", "Location has changed from the last one. We\'re fine.", 5);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
catch(NullPointerException ne)
|
||
|
{
|
||
|
/*
|
||
|
* Just null pointer exception. Ringbuffer isn't filled to its maximum, yet.
|
||
|
*/
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if(counter>0)
|
||
|
counter--;
|
||
|
else
|
||
|
counter = Settings.locationRingBufferSize-1;
|
||
|
} while(counter != locationRingBufferLastPosition);
|
||
|
|
||
|
Miscellaneous.logEvent("w", "Ringbuffer", "Location has not changed from the last one. Something\'s odd. Maybe the location engine kind of hung up.", 2);
|
||
|
return false;
|
||
|
}
|
||
|
catch(ArrayIndexOutOfBoundsException e)
|
||
|
{
|
||
|
Miscellaneous.logEvent("e", "Ringbuffer", "Probably not enough values, yet.", 5);
|
||
|
return true;
|
||
|
}
|
||
|
catch(Exception e)
|
||
|
{
|
||
|
Miscellaneous.logEvent("e", "Ringbuffer", "Error in ringbuffer: " + Log.getStackTraceString(e), 4);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static class GpsComparisonLocationListener implements LocationListener
|
||
|
{
|
||
|
public AutomationService parent = null;
|
||
|
|
||
|
@Override
|
||
|
public void onLocationChanged(Location up2DateLocation)
|
||
|
{
|
||
|
stopGpsMeasurement();
|
||
|
|
||
|
PointOfInterest.positionUpdate(up2DateLocation, parent, true, false);
|
||
|
|
||
|
Miscellaneous.logEvent("i", "LocationListener", "Disarmed location listener, accuracy reached", 4);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onProviderDisabled(String provider)
|
||
|
{
|
||
|
// TODO Auto-generated method stub
|
||
|
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onProviderEnabled(String provider)
|
||
|
{
|
||
|
// TODO Auto-generated method stub
|
||
|
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onStatusChanged(String provider, int status, Bundle extras)
|
||
|
{
|
||
|
// TODO Auto-generated method stub
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void startGpsMeasurement(AutomationService parentService)
|
||
|
{
|
||
|
// Arm location updates
|
||
|
if(!gpsLocationListenerArmed)
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "PointOfInterest", "Unsatisfactory accuracy of network location. Performing comparison measurement via GPS.", 3);
|
||
|
|
||
|
String myGpsComparisonProviderName;
|
||
|
|
||
|
if(Settings.privacyLocationing)
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", "PointOfInterest", parentService.getResources().getString(R.string.enforcingGps), 4);
|
||
|
myGpsComparisonProviderName = LocationManager.GPS_PROVIDER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Miscellaneous.logEvent("i", "PointOfInterest", parentService.getResources().getString(R.string.notEnforcingGps), 4);
|
||
|
Criteria crit = new Criteria();
|
||
|
// crit.setPowerRequirement(Criteria.POWER_LOW);
|
||
|
// crit.setAltitudeRequired(false);
|
||
|
// crit.setSpeedRequired(false);
|
||
|
// crit.setBearingRequired(false);
|
||
|
crit.setCostAllowed(true);
|
||
|
crit.setAccuracy(Criteria.ACCURACY_FINE);
|
||
|
gpsComparisonLocationManager = (LocationManager)parentService.getSystemService(parentService.LOCATION_SERVICE);
|
||
|
myGpsComparisonProviderName = gpsComparisonLocationManager.getBestProvider(crit, true);
|
||
|
}
|
||
|
|
||
|
Miscellaneous.logEvent("i", "LocationListener", "Arming location listener, Provider: " + myGpsComparisonProviderName, 4);
|
||
|
gpsComparisonLocationListener = new GpsComparisonLocationListener();
|
||
|
gpsComparisonLocationListener.parent = parentService;
|
||
|
gpsComparisonLocationManager.requestLocationUpdates(myGpsComparisonProviderName, Settings.minimumTimeBetweenUpdate, Settings.minimumDistanceChangeForNetworkUpdate, gpsComparisonLocationListener);
|
||
|
gpsLocationListenerArmed = true;
|
||
|
|
||
|
// set timeout
|
||
|
Message message = new Message();
|
||
|
message.what = 1;
|
||
|
Miscellaneous.logEvent("i", parentService.getResources().getString(R.string.gpsComparison), parentService.getResources().getString(R.string.startingGpsTimeout), 4);
|
||
|
if(timeoutHandler.parentService == null)
|
||
|
timeoutHandler.parentService = parentService;
|
||
|
timeoutHandler.sendMessageDelayed(message, Settings.gpsTimeout * 1000);
|
||
|
timeoutHandlerActive = true;
|
||
|
}
|
||
|
else
|
||
|
Miscellaneous.logEvent("i", "PointOfInterest", "Comparison measurement via GPS requested, but already active.", 3);
|
||
|
}
|
||
|
|
||
|
private static void stopGpsMeasurement()
|
||
|
{
|
||
|
if(gpsLocationListenerArmed)
|
||
|
{
|
||
|
gpsComparisonLocationManager.removeUpdates(gpsComparisonLocationListener);
|
||
|
gpsLocationListenerArmed = false;
|
||
|
}
|
||
|
|
||
|
if(timeoutHandlerActive)
|
||
|
{
|
||
|
timeoutHandler.removeMessages(1);
|
||
|
timeoutHandlerActive = false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
private static class TimeoutHandler extends Handler
|
||
|
{
|
||
|
public AutomationService parentService = null;
|
||
|
public Location locationToApplyIfGpsFails = null;
|
||
|
|
||
|
@Override
|
||
|
public void handleMessage(Message msg)
|
||
|
{
|
||
|
super.handleMessage(msg);
|
||
|
|
||
|
if(msg.what == 1)
|
||
|
{
|
||
|
Miscellaneous.logEvent("i", parentService.getResources().getString(R.string.gpsComparison), parentService.getResources().getString(R.string.gpsComparisonTimeoutStop), 4);
|
||
|
stopGpsMeasurement();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private boolean plausibilityCheck()
|
||
|
{
|
||
|
double distance, minimumDistance, overlap;
|
||
|
|
||
|
if(this.getName().equals("null"))
|
||
|
{
|
||
|
// Invalid name
|
||
|
String text = Miscellaneous.getAnyContext().getResources().getString(R.string.invalidPoiName);
|
||
|
Miscellaneous.logEvent("w", "POI", text, 2);
|
||
|
Toast.makeText(Miscellaneous.getAnyContext(), text, Toast.LENGTH_LONG).show();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for(PointOfInterest otherPoi : this.getPointOfInterestCollection())
|
||
|
{
|
||
|
distance = otherPoi.getLocation().distanceTo(this.getLocation());
|
||
|
minimumDistance = otherPoi.getRadius()/2 + this.getRadius()/2;
|
||
|
overlap = Math.round(Math.abs(distance - minimumDistance));
|
||
|
|
||
|
if(distance <= minimumDistance && !otherPoi.getName().equals(this.getName()))
|
||
|
{
|
||
|
String text = String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.overlapBetweenPois), otherPoi.getName(), String.valueOf(overlap));
|
||
|
Miscellaneous.logEvent("w", "POI", text, 2);
|
||
|
Toast.makeText(Miscellaneous.getAnyContext(), text, Toast.LENGTH_LONG).show();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
Miscellaneous.logEvent("w", "POI", Miscellaneous.getAnyContext().getResources().getString(R.string.noOverLap), 2);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public static boolean reachedPoiWithActivateWifiRule()
|
||
|
{
|
||
|
PointOfInterest activePoi = PointOfInterest.getActivePoi();
|
||
|
if(activePoi != null)
|
||
|
{
|
||
|
for(Rule rule : Rule.findRuleCandidatesByPoi(activePoi, true))
|
||
|
{
|
||
|
for(Action action : rule.getActionSet())
|
||
|
{
|
||
|
if(action.getAction().equals(Action.Action_Enum.setWifi) && action.getParameter1())
|
||
|
{
|
||
|
// We are at a POI that specifies to enable wifi.
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|