/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.location;
import android.content.Context;
import android.location.Criteria;
import android.location.GpsStatus;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import com.codename1.impl.android.AndroidImplementation;
import com.codename1.io.Log;
import static com.codename1.location.LocationManager.AVAILABLE;
import static com.codename1.location.LocationManager.OUT_OF_SERVICE;
import static com.codename1.location.LocationManager.TEMPORARILY_UNAVAILABLE;
import com.codename1.ui.Display;
import java.io.IOException;
/**
*
* @author Chen
*/
public class AndroidLocationManager extends com.codename1.location.LocationManager implements android.location.LocationListener, GpsStatus.Listener {
private LocationManager locationManager;
private String bestProvider;
private Context context;
private boolean searchForProvider = false;
private static AndroidLocationManager instance;
private long lastLocationMillis;
private Location lastLocation;
private AndroidLocationManager(Context ctx) {
this.context = ctx;
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
public static AndroidLocationManager getInstance(Context context) {
if (instance == null) {
instance = new AndroidLocationManager(context);
}
return instance;
}
private String findProvider(boolean includeNetwork) {
String providerName = null;
Criteria criteria = new Criteria();
criteria.setSpeedRequired(true);
criteria.setAltitudeRequired(true);
LocationProvider provider;
boolean enabled;
if (includeNetwork) {
provider = locationManager.getProvider(LocationManager.NETWORK_PROVIDER);
enabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (provider != null && enabled) {
providerName = provider.getName();
} else {
providerName = locationManager.getBestProvider(criteria, true);
}
}
if (providerName == null) {
// If GPS provider, then create and start GPS listener
provider = locationManager.getProvider(LocationManager.GPS_PROVIDER);
enabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (provider != null && enabled) {
providerName = provider.getName();
}
}
return providerName;
}
public Location getCurrentLocation() throws IOException {
if (lastLocation != null) {
return lastLocation;
}
try {
String provider = findProvider(true);
android.location.Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
return convert(location);
}
} catch (Exception err) {
Log.e(err);
throw new IOException(err);
}
throw new IOException("cannot retrieve location try later");
}
public void bindListener() {
bestProvider = findProvider(false);
if (bestProvider != null) {
startListenToGPS();
} else {
searchForProvider = true;
setLocationManagerStatus(OUT_OF_SERVICE);
Thread t = new Thread() {
public void run() {
while (searchForProvider) {
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
//keep try to get the gps provider, it is very likely
//that the app is requesting the user to turn on the gps
bestProvider = findProvider(false);
if (bestProvider != null) {
setLocationManagerStatus(AVAILABLE);
startListenToGPS();
return;
}
}
}
};
t.setUncaughtExceptionHandler(AndroidImplementation.exceptionHandler);
t.start();
}
}
private void startListenToGPS() {
Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.post(new Runnable() {
public void run() {
locationManager.requestLocationUpdates(bestProvider, 0, 0, AndroidLocationManager.this);
locationManager.addGpsStatusListener(AndroidLocationManager.this);
}
});
}
public void clearListener() {
searchForProvider = false;
Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.post(new Runnable() {
public void run() {
locationManager.removeUpdates(AndroidLocationManager.this);
locationManager.removeGpsStatusListener(AndroidLocationManager.this);
}
});
}
public static Location convert(android.location.Location loc) {
Location retVal = new Location();
retVal.setAccuracy(loc.getAccuracy());
retVal.setAltitude(loc.getAltitude());
retVal.setLatitude(loc.getLatitude());
retVal.setLongitude(loc.getLongitude());
retVal.setTimeStamp(loc.getTime());
retVal.setVelocity(loc.getSpeed());
retVal.setDirection(loc.getBearing());
return retVal;
}
public void onLocationChanged(final android.location.Location loc) {
synchronized (this) {
final com.codename1.location.LocationListener l = getLocationListener();
if (l != null) {
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
lastLocation = convert(loc);
l.locationUpdated(lastLocation);
lastLocationMillis = SystemClock.elapsedRealtime();
}
});
}
}
}
public void onGpsStatusChanged(int event) {
boolean isGPSFix = false;
switch (event) {
case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
if (lastLocation != null) {
isGPSFix = (SystemClock.elapsedRealtime() - lastLocationMillis) < 10000;
}
if (isGPSFix) { // A fix has been acquired.
setLocationManagerStatus(AVAILABLE);
} else { // The fix has been lost.
setLocationManagerStatus(TEMPORARILY_UNAVAILABLE);
}
break;
case GpsStatus.GPS_EVENT_FIRST_FIX:
setLocationManagerStatus(AVAILABLE);
break;
}
}
public void onStatusChanged(String provider, int status, Bundle extras) {
int s = convertStatus(status);
setLocationManagerStatus(s);
}
private int convertStatus(int status) {
switch (status) {
case LocationProvider.AVAILABLE:
return com.codename1.location.LocationManager.AVAILABLE;
case LocationProvider.OUT_OF_SERVICE:
return com.codename1.location.LocationManager.OUT_OF_SERVICE;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
return com.codename1.location.LocationManager.TEMPORARILY_UNAVAILABLE;
}
return com.codename1.location.LocationManager.OUT_OF_SERVICE;
}
public void onProviderEnabled(String provider) {
}
public void onProviderDisabled(String provider) {
}
public Location getLastKnownLocation() {
if (bestProvider == null) {
String provider = locationManager.getBestProvider(new Criteria(), false);
android.location.Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
return convert(location);
}
}
try {
return getCurrentLocation();
} catch (Exception e) {
}
return null;
}
private void setLocationManagerStatus(final int status) {
int current = getStatus();
if (current != status) {
setStatus(status);
synchronized (this) {
Display.getInstance().callSerially(new Runnable() {
@Override
public void run() {
com.codename1.location.LocationListener l = getLocationListener();
if (l != null) {
l.providerStateChanged(status);
}
}
});
}
}
}
public boolean isGPSDetectionSupported() {
return true;
}
public boolean isGPSEnabled() {
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
}