package com.jakehilborn.speedr; import android.location.Location; import com.jakehilborn.speedr.utils.UnitUtils; public class StatsCalculator { //Time values in nanoseconds //Speed values in meters / second private Location location; private Location prevLocation; private Double limit; private long firstLimitTime; private long prevLimitTime; private Location prevLimitLocation; //The location when the most recent speed limit was fetched private boolean forceLimitStale; private boolean networkDown; private long prevNetworkCheckTime; private double timeSaved; public interface Callback { void handleNetworkUpdate(); } //Callback for StatsCalculator to push speed limit updates to MainService public Callback callback; public void setCallback(Callback callback) { this.callback = callback; } public void setLocation(Location location) { prevLocation = this.location; this.location = location; } public Double getSpeed() { if (location == null) return null; return (double) this.location.getSpeed(); } public long getFirstLimitTime() { return firstLimitTime; } public boolean isLimitStale() { //1st Limit hasn't been fetched yet, avoiding NPE below if (prevLimitLocation == null || prevLimitTime == 0) return true; if (forceLimitStale) { forceLimitStale = false; return true; } if (networkDown) { return true; } //Stale if previous Limit request was over 5 seconds ago and the user has traveled over 25 meters since the previous Limit request return (prevLimitTime + UnitUtils.secondsToNanos(5) < System.nanoTime() && location.distanceTo(prevLimitLocation) > 25); } public void setForceLimitStale(boolean forceLimitStale) { this.forceLimitStale = forceLimitStale; } public boolean isNetworkCheckStale() { return prevNetworkCheckTime + UnitUtils.secondsToNanos(10) < System.nanoTime() //Check is network down at most every 10s && prevLimitTime + UnitUtils.secondsToNanos(10) < System.nanoTime(); //Only check if a new speed limit hasn't been received in over 10s } public boolean isNetworkDown() { return this.networkDown; } public void setNetworkDown(boolean networkDown) { this.prevNetworkCheckTime = System.nanoTime(); this.networkDown = networkDown; } public Double getLimit() { return this.limit; } //When limit is set to null it means there is no speed limit data available so continue showing existing speed limit. //When limit is set to 0 it means there is no speed limit data available, set speed limit to missing. public void setLimit(Double limit, Double lat, Double lon) { prevLimitTime = System.nanoTime(); if (limit != null) { this.limit = limit; if (firstLimitTime == 0 && limit != 0) firstLimitTime = prevLimitTime; } prevLimitLocation = new Location("fused"); prevLimitLocation.setLatitude(lat); prevLimitLocation.setLongitude(lon); networkDown = false; callback.handleNetworkUpdate(); } public double getTimeSaved() { return timeSaved; } public void setTimeSaved(double timeSaved) { this.timeSaved = timeSaved; } public void calcTimeSaved() { if (prevLimitTime == 0 //Limit data not available yet || prevLocation == null //Less than 2 speed data points have been captured || limit == null || limit == 0) return; //Avoiding divide by zero double currentDiff = ((location.getElapsedRealtimeNanos() - prevLocation.getElapsedRealtimeNanos()) * (location.getSpeed() - limit)) / limit; if (currentDiff > 0) timeSaved += currentDiff; } }