251 lines
8.7 KiB
Java

package com.cradle.iitc_mobile;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.Surface;
import com.cradle.iitc_mobile.compass.Compass;
import com.cradle.iitc_mobile.compass.CompassListener;
public class IITC_UserLocation implements CompassListener, LocationListener {
private static final int TWO_MINUTES = 1000 * 60 * 2;
private final Compass mCompass;
private boolean mFollowing = false;
private final IITC_Mobile mIitc;
private Location mLastLocation = null;
private final LocationManager mLocationManager;
private boolean mLocationRegistered = false;
private int mMode = 0;
private double mOrientation = 0;
private boolean mOrientationRegistered = false;
private boolean mRunning = false;
public IITC_UserLocation(final IITC_Mobile iitc) {
mIitc = iitc;
mCompass = Compass.getDefaultCompass(mIitc);
// Acquire a reference to the Location Manager and Sensor Manager
mLocationManager = (LocationManager) iitc.getSystemService(Context.LOCATION_SERVICE);
}
// Checks whether two providers are the same
private boolean isSameProvider(final String provider1, final String provider2) {
if (provider1 == null) { return provider2 == null; }
return provider1.equals(provider2);
}
private void setOrientation(Double orientation) {
// we have a transition defined for the rotation
// changes to the orientation should always be less than 180°
if (orientation != null) {
while (orientation < mOrientation - 180)
orientation += 360;
while (orientation > mOrientation + 180)
orientation -= 360;
mOrientation = orientation;
}
mIitc.getWebView().loadJS("if(window.plugin && window.plugin.userLocation)"
+ "window.plugin.userLocation.onOrientationChange(" + String.valueOf(orientation) + ");");
}
private void updateListeners() {
final boolean useLocation = mRunning && mMode != 0 && !mIitc.isLoading();
final boolean useOrientation = useLocation && mMode == 2;
if (useLocation && !mLocationRegistered) {
try {
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
} catch (final IllegalArgumentException e) {
// if the given provider doesn't exist
Log.w(e);
}
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
} catch (final IllegalArgumentException e) {
// if the given provider doesn't exist
Log.w(e);
}
mLocationRegistered = true;
}
if (!useLocation && mLocationRegistered) {
mLocationManager.removeUpdates(this);
mLocationRegistered = false;
}
if (useOrientation && !mOrientationRegistered) {
mCompass.registerListener(this);
mOrientationRegistered = true;
}
if (!useOrientation && mOrientationRegistered) {
mCompass.unregisterListener(this);
mOrientationRegistered = false;
}
}
/**
* Determines whether one Location reading is better than the current Location fix
*
* @param location
* The new Location that you want to evaluate
* @param currentBestLocation
* The current Location fix, to which you want to compare the new one
*
* code copied from http://developer.android.com/guide/topics/location/strategies.html#BestEstimate
*/
protected boolean isBetterLocation(final Location location, final Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
final long timeDelta = location.getTime() - currentBestLocation.getTime();
final boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
final boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
final boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) { return false; }
// Check whether the new location fix is more or less accurate
final int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
final boolean isLessAccurate = accuracyDelta > 0;
final boolean isMoreAccurate = accuracyDelta < 0;
final boolean isSignificantlyLessAccurate = accuracyDelta > 100;
// Check if the old and new location are from the same provider
final boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) { return true; }
return false;
}
public boolean hasCurrentLocation() {
if (!mLocationRegistered) return false;
return mLastLocation != null;
}
public boolean isFollowing() {
return mFollowing;
}
public void locate(final boolean persistentZoom) {
// do not touch the javascript while iitc boots
if (mIitc.isLoading()) return;
final Location location = mLastLocation;
if (location == null) return;
mIitc.getWebView().loadJS("if(window.plugin && window.plugin.userLocation)"
+ "window.plugin.userLocation.locate("
+ location.getLatitude() + ", " + location.getLongitude() + ", "
+ location.getAccuracy() + ", " + persistentZoom + ");");
}
@Override
public void onCompassChanged(final float x, final float y, final float z) {
double orientation = Math.toDegrees(x);
final int rotation = mIitc.getWindowManager().getDefaultDisplay().getRotation();
switch (rotation) {
case Surface.ROTATION_90:
orientation += 90;
break;
case Surface.ROTATION_180:
orientation += 180;
break;
case Surface.ROTATION_270:
orientation += 270;
break;
}
setOrientation(orientation);
}
public void onLoadingStateChanged() {
updateListeners();
}
@Override
public void onLocationChanged(final Location location) {
if (!isBetterLocation(location, mLastLocation)) return;
mLastLocation = location;
// do not touch the javascript while iitc boots
if (mIitc.isLoading()) return;
mIitc.getWebView().loadJS("if(window.plugin && window.plugin.userLocation)"
+ "window.plugin.userLocation.onLocationChange("
+ location.getLatitude() + ", " + location.getLongitude() + ");");
}
@Override
public void onProviderDisabled(final String provider) {
}
@Override
public void onProviderEnabled(final String provider) {
}
public void onStart() {
mRunning = true;
updateListeners();
// in case we just switched from loc+sensor to loc-only, let javascript know
if (mMode == 1) {
setOrientation(null);
}
}
@Override
public void onStatusChanged(final String provider, final int status, final Bundle extras) {
}
public void onStop() {
mRunning = false;
updateListeners();
}
public void reset() {
setFollowMode(false);
}
public void setFollowMode(final boolean follow) {
mFollowing = follow;
mIitc.invalidateOptionsMenu();
}
/**
* set the location mode to use. Available modes:
* 0: don't show user's position
* 1: show user's position
* 2: show user's position and orientation
*
* @return whether a reload is needed to reflect the changes made to the preferences
*/
public boolean setLocationMode(final int mode) {
final boolean needsReload = (mode == 0 && mMode != 0) || (mode != 0 && mMode == 0);
mMode = mode;
return needsReload;
}
}