I want to capture current location (latitude and longitude) of android device after specific interval (say 30 mins).. My class (or service ?? not sure what to use ) will start listening to LocationManagerListener when device booting completed. What is the best way of implementing this? how we can make use of locationChanged() method in this scenario?
This is what i think it can go:
Listen for boot completed event and set alarm service:
public class OnBootReceiver extends BroadcastReceiver {
private static final int PERIOD=1800000; // 30 minutes
#Override
public void onReceive(Context context, Intent intent) {
AlarmManager mgr=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i=new Intent(context, OnAlarmReceiver.class);
PendingIntent pi=PendingIntent.getBroadcast(context, 0,
i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime()+60000,
PERIOD,
pi);
}
}
Listen for alarm service and initiate the location capture class or service:
public class OnAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
WakefulIntentService.acquireStaticLock(context);
context.startService(new Intent(context, locationCapture.class));
or
new locationCapture().classmethod();
}
}
I am not sure how locationCapture class should be implemented. Should it be normal Java class or Service class?
Any help will be appreciated.
This the service class you can use it
public class ServiceLocation extends Service{
private LocationManager locMan;
private Boolean locationChanged;
private Handler handler = new Handler();
public static Location curLocation;
public static boolean isService = true;
LocationListener gpsListener = new LocationListener() {
public void onLocationChanged(Location location) {
if (curLocation == null) {
curLocation = location;
locationChanged = true;
}else if (curLocation.getLatitude() == location.getLatitude() && curLocation.getLongitude() == location.getLongitude()){
locationChanged = false;
return;
}else
locationChanged = true;
curLocation = location;
if (locationChanged)
locMan.removeUpdates(gpsListener);
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status,Bundle extras) {
if (status == 0)// UnAvailable
{
} else if (status == 1)// Trying to Connect
{
} else if (status == 2) {// Available
}
}
};
#Override
public void onCreate() {
super.onCreate();
curLocation = getBestLocation();
if (curLocation == null)
Toast.makeText(getBaseContext(),"Unable to get your location", Toast.LENGTH_SHORT).show();
else{
//Toast.makeText(getBaseContext(), curLocation.toString(), Toast.LENGTH_LONG).show();
}
isService = true;
}
final String TAG="LocationService";
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onLowMemory() {
super.onLowMemory();
}
#Override
public void onStart(Intent i, int startId){
handler.postDelayed(GpsFinder,1);
}
#Override
public void onDestroy() {
handler.removeCallbacks(GpsFinder);
handler = null;
Toast.makeText(this, "Stop services", Toast.LENGTH_SHORT).show();
isService = false;
}
public IBinder onBind(Intent arg0) {
return null;
}
public Runnable GpsFinder = new Runnable(){
public void run(){
Location tempLoc = getBestLocation();
if(tempLoc!=null)
curLocation = tempLoc;
tempLoc = null;
handler.postDelayed(GpsFinder,1000);// register again to start after 1 seconds...
}
};
private Location getBestLocation() {
Location gpslocation = null;
Location networkLocation = null;
if(locMan==null){
locMan = (LocationManager) getApplicationContext() .getSystemService(Context.LOCATION_SERVICE);
}
try {
if(locMan.isProviderEnabled(LocationManager.GPS_PROVIDER)){
locMan.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000, 1, gpsListener);// here you can set the 2nd argument time interval also that after how much time it will get the gps location
gpslocation = locMan.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
if(locMan.isProviderEnabled(LocationManager.NETWORK_PROVIDER)){
locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,1000, 1, gpsListener);
networkLocation = locMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
}
} catch (IllegalArgumentException e) {
//Log.e(ErrorCode.ILLEGALARGUMENTERROR, e.toString());
Log.e("error", e.toString());
}
if(gpslocation==null && networkLocation==null)
return null;
if(gpslocation!=null && networkLocation!=null){
if(gpslocation.getTime() < networkLocation.getTime()){
gpslocation = null;
return networkLocation;
}else{
networkLocation = null;
return gpslocation;
}
}
if (gpslocation == null) {
return networkLocation;
}
if (networkLocation == null) {
return gpslocation;
}
return null;
}
}
You can set time into the handler or into the requestLocationUpdates(). you need to start this service from home. As into I have set the 1 sec for both in handler and requestLocationUpdate() for getting location after 1 sec and update me.
Edited:
As this service for getting current location of the user you can start from home activity and also start from boot time. With the home activity sure that if the service was stop by the user using another application like task killer then when the user launch your application then from the home this service will be start again, to start service you can do like this way
startService(new Intent(YourActivity.this,ServiceLocation.class));
When you need to stop service it will call the onDestroy() so the handler can cancel the thread to continue for getting the location.
As the GPS set with the 1 sec(1000) this will getting the gps location every 1 sec but for getting that location you need to call every time and in my case i have set to 1 sec and as per your requirement set to 30 sec. So you gps getting the location every 1 sec and using this service in handler set the 30 min. for saving battery life you can also set the different time in to the gps request method so it will save the battery life.
you can remove the comparison part of location and current location because locationlistener every time call when the location was changed and you can use the curLocation anywhere in your application to get the current location but be sure first that you have to start this service first then after you can use otherwise you getting null pointer exception when you access the latitude and longitude of this object
Hope you got some idea and I got the answer about your queries
This guide with source codes for various API levels comes from source:
http://android-developers.blogspot.com/search/label/Location
Related
I posted my Requirement,Problem and code,I don't know what is wrong with my code,Any help..
My Requirement : I want to get latitude,longtitude from GPS every 10 minutes
Problem : I am Keep on Getting latitude,longtitude
My Code :
1].I am using this code in activity,I am starting Broadcast Receiver from activity
//Pending Intent
Intent in = new Intent(this,GPSReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, in, 0);
//Alaram manager
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
am.setRepeating(AlarmManager.RTC, cal.getTimeInMillis(), 10*60*1000, pi);
2].BroadCast Class "GPSReceiver.class",From here I am starting service names as GPSService
public class GPSReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context,GPSService.class);
context.startService(service);
}
}
3].Service Class "GPSService",This is for getting location updates
public class GPSService extends Service {
#Override
public IBinder onBind(Intent arg0) {
return null;
}
//Flag to know GPS Status
boolean isGPSEnabled = false;
LocationManager locationmanager;
String locationgpsprovider = LocationManager.GPS_PROVIDER;
double latitude;
double longtitude;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//To get Location service
locationmanager = (LocationManager) getSystemService(LOCATION_SERVICE);
//To get GPS Status
isGPSEnabled = locationmanager.isProviderEnabled(locationgpsprovider);
if (!isGPSEnabled) {
Toast.makeText(this, "Please check your GPS connection", Toast.LENGTH_LONG).show();
}else {
locationmanager.requestLocationUpdates(locationgpsprovider, 0, 0, ll);
}
return START_NOT_STICKY;
}
LocationListener ll = new LocationListener() {
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onLocationChanged(Location location) {
Log.i("Hei", "10 minutes up");
latitude = location.getLatitude();
longtitude = location.getLongitude();
}
};
Everythime you alarm goes on it delivers another Intent to Service and locationmanager starts all over again. You don't need an Alarm here since LocationManager already deliveries location at specified intervals.
It's enough starting your Service only once and instantiate your locationmanager stuff at onCreate. In this case use onStartCommand only to return START_NOT_STICKY.
Tip 1: Define a condition to stop the service, don't let it run forever.
Tip 2:Take a look at requestLocationUpdates javadoc to setup a proper minTime.
Tip 3:You can find more tips in Android docs:
http://developer.android.com/training/location/index.html
I have a LocationService which starts onResume() of the MainActivity and stops onDestroy().
#Override
protected void onResume() {
super.onResume();
//Start the service using alaram manager
//If its not running currently
if (isLocationServiceRunning(this)) {
am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent(this, LocationService.class);
pi = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pi);
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), 1 * 60 * 1000, pi);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (isLocationServiceRunning(this)) {
stopService(new Intent(this, LocationService.class));
if (am != null && pi != null) {
am.cancel(pi);
}
}
}
LocationService.java
public class LocationService extends Service implements LocationListener {
public static double curLat = 0.0;
public static double curLng = 0.0;
private LocationManager mgr;
private String best;
private Location location;
private Location currentBestLocation;
private static final int TWO_MINUTES = 1000 * 60 * 2;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mgr = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean gps_enabled = mgr
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (gps_enabled) {
// If GPS is enabled, set criteria as ACCURACY_FINE
// and get the best provider(which usually will be GPS_PROVIDER)
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
best = mgr.getBestProvider(criteria, true);
// getLastKnownLocation so that user don't need to wait
location = mgr.getLastKnownLocation(best);
if (location == null) {
// request for a single update, and try again.
// Later will request for updates every 10 mins
mgr.requestSingleUpdate(criteria, this, null);
location = mgr
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
if (location != null) {
// If the GPS gives a location, update curLat and curLng
dumpLocation(location);
} else {
// If the location is still null, go for NETWORK_PROVIDER
best = LocationManager.NETWORK_PROVIDER;
location = mgr.getLastKnownLocation(best);
if (location != null) {
// If the NETWORK gives a location, update curLat and curLng
dumpLocation(location);
}
}
// Register the Location Manager for updates, with both the
// providers
// Since GPS updates are expensive, we ask update every 10 mins and
// unregister updates if GPS is disabled in onProviderDisabled
// callback
mgr.requestLocationUpdates(LocationManager.GPS_PROVIDER,
10 * 60 * 1000, 50, this);
// NETWORK_PROVIDER updates every 20 secs
mgr.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
20 * 1000, 0, this);
return START_NOT_STICKY;
} else {
// If GPS is disables, go with NETWORK_PROVIDER
best = LocationManager.NETWORK_PROVIDER;
location = mgr.getLastKnownLocation(best);
if (location != null) {
dumpLocation(location);
}
// Register NETWORK_PROVIDER for updates every 20 secs
mgr.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
20 * 1000, 0, this);
return START_NOT_STICKY;
}
}
private void dumpLocation(Location l) {
// Called to update the curLat and curLng.
currentBestLocation = l;
SimpleDateFormat s = new SimpleDateFormat("dd/MM/yyyy:hh:mm:ss",
Locale.ENGLISH);
String format = s.format(l.getTime());
try {
Geocoder coder = new Geocoder(this);
List<Address> address;
Address location = null;
address = coder.getFromLocation(l.getLatitude(), l.getLongitude(),
1);
location = address.get(0);
} catch (Exception e) {
Log.e("Exception while getting address", e.getMessage() + "");
}
curLat = l.getLatitude();
curLng = l.getLongitude();
}
#Override
public void onLocationChanged(Location location) {
// called when location is changed, since we registered Location
// Providers
// for updates
if (isBetterLocation(location, currentBestLocation)) {
dumpLocation(location);
} else {
Log.d("Not a Better Location", "Ignore");
}
}
#Override
public void onProviderDisabled(String provider) {
// Check if best(the currently being used provider) is not null
if (best != null) {
// if best and disabled provider are same, the remove updates
if ((provider.equalsIgnoreCase(LocationManager.GPS_PROVIDER) && best
.equals(LocationManager.GPS_PROVIDER))
|| provider
.equalsIgnoreCase(LocationManager.NETWORK_PROVIDER)
&& best.equals(LocationManager.NETWORK_PROVIDER)) {
if (mgr != null) {
mgr.removeUpdates(this);
}
}
}
}
#Override
public void onProviderEnabled(String provider) {
// This will be taken care in the onStartCommand where if gps_enabled
// case is used.
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// No need to care about, because any thing like OUT_OF_SERVICE occurs,
// location being fetched will be null and such cases are handled above.
if ((provider.equals(LocationManager.GPS_PROVIDER))
&& (LocationProvider.OUT_OF_SERVICE == status)) {
if (mgr != null) {
mgr.removeUpdates(this);
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
// triggered when we call stopService(LocationService);
// which is done in onDestroy of MainActivity
// Because LocationService must be stopped
// when application is closed to avoid data usage
if (mgr != null) {
mgr.removeUpdates(this);
}
}
protected boolean isBetterLocation(Location location,
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
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
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
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Not significantly newer or older, so check for Accuracy
if (isMoreAccurate) {
// If more accurate return true
return true;
} else if (isNewer && !isLessAccurate) {
// Same accuracy but newer, return true
return true;
} else if (isNewer && !isSignificantlyLessAccurate
&& isFromSameProvider) {
// Accuracy is less (not much though) but is new, so if from same
// provider return true
return true;
}
return false;
}
// Checks whether two providers are the same
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
}
The service surely starts and stops as expected and I can see the location details in log, which are fine.
The problem if when I move to a complete different location(300 miles), the curLat and curLng values still remain as that of the old, when I open the application.
Is it because I am not running the service when the device is in motion(because my application is not running)?
Because when I open some other application like FourSquare(which gets the correct location) and then reopen my application, then it shows the correct location.
What else should I do to refresh the location properly.
I think your problem is here
best = mgr.getBestProvider(criteria, true);
// getLastKnownLocation so that user don't need to wait
location = mgr.getLastKnownLocation(best);
if (location == null) {
// request for a single update, and try again.
// Later will request for updates every 10 mins
mgr.requestSingleUpdate(criteria, this, null);
location = mgr
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
because there was previously a location location = mgr.getLastKnownLocation(best); returns that location without starting the provider (see the android documentation. So the location is not null and mgr.requestSingleUpdate(criteria, this, null); is never run.
To get up to date location data a provider must be started.
so a correction could be:
best = mgr.getBestProvider(criteria, true);
// getLastKnownLocation so that user don't need to wait
mgr.requestSingleUpdate(best, this, null);
location = mgr.getLastKnownLocation(best);
Also I'm not sure if it is intended but this service will use the network provider even when GPS data is available and more accurate (due to the 10 minute and 2 minute times chosen for GPS updates and data obsolescence.
P.S. Is there a specific reason you do not want to use FusedLocationProvider that is part of Google Play Services? I have found it to be simpler and it is supposedly optimized for selected best providers and conserving battery.
You code looks perfectly fine if you want to get the location in the foreground. I have gone through in the deep and get to know that in the onDestroy you have stopped the service and alarms also. hence as and when the current app is going to background and the onDestroy is called by system then the code fails to update the location in the background. more over when you launch the application again it will start the service and very first time get the older location which was cached.
when other application updates the location you will get that location according to documentation of the mgr.getLastKnownLocation(best).
Hence to solve this problem do not use alarm here to start service in repeating manner or destory it.
simply start the service and in the onStartCommand ask for the update of the location. and if you want to get rid of the location updates, use removeLocationUpdates(LocationListener) .
Examples are given here http://developer.android.com/training/location/receive-location-updates.html
My best guess is dump "isBetterLocation" and try without it to see what will happen. Based on those checks (which are rather complicated), I think the mistake is either in "isSignificantlyOlder" or in the last return statement (otherwise you would get the new location, correct?)
Have you debugged it to check if the current logic is correct, and if it is, for what distances?
Here is an example to receive location update using Google Play Services
This is MyActivity class
public class MyActivity extends Activity implements
ConnectionCallbacks, OnConnectionFailedListener {
public static final int PLAY_SERVICES_NOT_AVAILABLE_REQUEST = 9000;
public static final int CONNECTION_FAILED_REQUEST = 1000;
private LocationClient mLocationClient;
private LocationRequest mLocationrequest;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_myactivity);
LocationManager mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationClient = new LocationClient(this, this, this);
boolean isGPSEnabled = mLocationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
boolean isNetworkEnabled = mLocationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
Toast.makeText(this, "GPS: " + isGPSEnabled, Toast.LENGTH_SHORT).show();
Toast.makeText(this, "Network: " + isNetworkEnabled, Toast.LENGTH_SHORT)
.show();
if (isGooglePlayServicesAvailable()) {
mLocationClient.connect();
} else {
// play services not available
}
}
private void defineLocationRequest() {
mLocationrequest = new LocationRequest();
mLocationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(5000);
}
private PendingIntent getCallBackIntent() {
return PendingIntent
.getService(getApplicationContext(), 0, new Intent(this,
MyIntentService.class),
PendingIntent.FLAG_UPDATE_CURRENT);
}
private boolean isGooglePlayServicesAvailable() {
int resultCode = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(this);
if (resultCode == ConnectionResult.SUCCESS) {
Log.d("Car Tracking", "play services available.");
return true;
} else {
Log.d("Car Tracking", "play services not available(resultCode:) "
+ resultCode);
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_NOT_AVAILABLE_REQUEST).show();
return false;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
switch (requestCode) {
case PLAY_SERVICES_NOT_AVAILABLE_REQUEST:
if (resultCode == Activity.RESULT_OK) {
// check again
}
break;
case CONNECTION_FAILED_REQUEST:
if (resultCode == Activity.RESULT_OK) {
// try to connect LocationClient Againg
}
break;
}
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
// TODO Auto-generated method stub
if (arg0.hasResolution()) {
try {
arg0.startResolutionForResult(this, CONNECTION_FAILED_REQUEST);
} catch (SendIntentException e) {
Log.d("TAG",
"Exception in resolving connection failed: "
+ e.toString());
}
}
}
#Override
public void onConnected(Bundle arg0) {
// TODO Auto-generated method stub
defineLocationRequest();
mLocationClient.requestLocationUpdates(mLocationrequest,
getCallBackIntent());
}
#Override
public void onDisconnected() {
// TODO Auto-generated method stub
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
mLocationClient.removeLocationUpdates(getCallBackIntent());
super.onDestroy();
}
}
Now, this is MyIntentService Class's onHandleIntent Method.
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
if (intent != null) {
Bundle extra = intent.getExtras();
Location location = (Location) extra
.get(LocationClient.KEY_LOCATION_CHANGED);
}
Here, the location object will give you most recent location update
Also add
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
in your manifest
You can use the LocationClient from Google Play Services, its easy to use and proven very efficient.
Here is the link to example
Use Fused Location Provider (new feature available since 4.2 - https://developer.android.com/google/play-services/location.html) - it just gets fast current location and sending updates.
Example: http://www.motta-droid.com/2013/11/location-requests-for-your-app-how-to.html
Just run singleton above in a Service and adjust location update params to your needs.
The only issue You should care about - if it can't determine your current location at all. For example, if just GPS location provider available to your device and you're indoors.
I observed your code..You are updating the location but you are not receiving the updated location information. here is the code how to get the location from a Service
// Send an Intent with an action named "custom-event-name". The Intent sent
// should
// be received by the ReceiverActivity.
private static void sendMessageToActivity(Location l, String msg) {
Intent intent = new Intent("GPSLocationUpdates");
// You can also include some extra data.
intent.putExtra("Status", msg);
Bundle b = new Bundle();
b.putParcelable("Location", l);
intent.putExtra("Location", b);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
in you main activity or which has to receive the location Info write this code.
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
mMessageReceiver, new IntentFilter("GPSLocationUpdates"));
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getBundleExtra("Location");
lastKnownLoc = (Location) b.getParcelable("Location");
if (lastKnownLoc != null) {
tvLatitude.setText(String.valueOf(lastKnownLoc.getLatitude()));
tvLongitude
.setText(String.valueOf(lastKnownLoc.getLongitude()));
}
}
};
I hope this will work...
I you do not mind waiting for GPS to achieve a first-fix this might help you. The first-fix should only be a matter of seconds if a fix have been found recently.
I have implemented some code that sends callback as soon as there is a first-fix and on locationchange based on GPSTracker from http://www.androidhive.info/2012/07/android-gps-location-manager-tutorial/.
With this implementation you can do:
private GPSTracker gps;
private FirstFixListener firstFixListener;
private LocationUpdateListener locationUpdateListener;
private void startGPS() {
gps = GPSTracker.getInstance(context);
// create listeners
firstFixListener = new MyFirstFixListener();
locationUpdateListener = new MyLocationUpdateListener();
// start the gps
gps.startUsingGPS(firstFixListener, locationUpdateListener);
}
private void stopGPS() {
// stop the gps and unregister callbacks
gps.stopUsingGPS(firstFixListener, locationUpdateListener);
}
private class MyFirstFixListener implements FirstFixListener {
#Override
public void onFirsFixChanged(boolean hasGPSfix) {
if (hasGPSfix == true) {
// accurate position
Location position = gps.getLocation();
}
}
}
private class MyLocationUpdateListener implements LocationUpdateListener {
#Override
public void onLocationChanged(Location location) {
// hand you each new location from the GPS
// you do not need this if you only want to get a single position
}
}
And here is my implementation of GPSTracker:
public class GPSTracker extends Service implements LocationListener {
private static final String TAG = "GPSTracker";
/**
* Register to receive callback on first fix status
*
* #author Morten
*
*/
public interface FirstFixListener {
/**
* Is called whenever gps register a change in first-fix availability
* This is valuable to prevent sending invalid locations to the server.
*
* #param hasGPSfix
*/
public void onFirsFixChanged(boolean hasGPSfix);
}
/**
* Register to receive all location updates
*
* #author Morten
*
*/
public interface LocationUpdateListener {
/**
* Is called every single time the GPS unit register a new location
* The location param will never be null, however, it can be outdated if hasGPSfix is not true.
*
* #param location
*/
public void onLocationChanged(Location location);
}
private Context mContext;
// flag for GPS status
private List<FirstFixListener> firstFixListeners;
private List<LocationUpdateListener> locationUpdateListeners;
boolean isGPSFix = false;
boolean isGPSEnabled = false;
private GPSFixListener gpsListener;
// flag for GPS status
boolean canGetLocation = false;
Location location; // location
double latitude; // latitude
double longitude; // longitude
long mLastLocationMillis;
private boolean logLocationChanges;
// Declaring a Location Manager
protected LocationManager locationManager;
/** removed again as we need multiple instances with different callbacks **/
private static GPSTracker instance;
public static GPSTracker getInstance(Context context) {
if (instance != null) {
return instance;
}
return instance = new GPSTracker(context);
}
private GPSTracker(Context context) {
this.mContext = context;
gpsListener = new GPSFixListener();
firstFixListeners = new ArrayList<GPSTracker.FirstFixListener>();
locationUpdateListeners = new ArrayList<GPSTracker.LocationUpdateListener>();
}
public boolean hasGPSFirstFix() {
return isGPSFix;
}
private void addFirstFixListener(FirstFixListener firstFixListener) {
this.firstFixListeners.add(firstFixListener);
}
private void addLocationUpdateListener(
LocationUpdateListener locationUpdateListener) {
this.locationUpdateListeners.add(locationUpdateListener);
}
private void removeFirstFixListener(FirstFixListener firstFixListener) {
this.firstFixListeners.remove(firstFixListener);
}
private void removeLocationUpdateListener(
LocationUpdateListener locationUpdateListener) {
this.locationUpdateListeners.remove(locationUpdateListener);
}
public void setLogLocationChanges(boolean logLocationChanges) {
this.logLocationChanges = logLocationChanges;
}
public Location getLocation() {
return location;
}
private Location startLocationListener() {
canGetLocation = false;
try {
locationManager = (LocationManager) mContext
.getSystemService(Service.LOCATION_SERVICE);
// getting GPS status
isGPSEnabled = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (isGPSEnabled) {
if (location == null) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, this);
locationManager.addGpsStatusListener(gpsListener);
if (locationManager != null) {
location = locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
} else {
showSettingsAlert();
}
} catch (Exception e) {
e.printStackTrace();
}
return location;
}
public void stopUsingGPS(FirstFixListener firstFixListener,
LocationUpdateListener locationUpdateListener) {
if (firstFixListener != null)
removeFirstFixListener(firstFixListener);
if (locationUpdateListener != null)
removeLocationUpdateListener(locationUpdateListener);
stopUsingGPS();
}
/**
* Stop using GPS listener Calling this function will stop using GPS in your
* app
* */
public void stopUsingGPS() {
Log.d("DEBUG", "GPS stop");
if (locationManager != null) {
locationManager.removeUpdates(GPSTracker.this);
location = null;
if (gpsListener != null) {
locationManager.removeGpsStatusListener(gpsListener);
}
}
isGPSFix = false;
location = null;
}
public void startUsingGPS(FirstFixListener firstFixListener,
LocationUpdateListener locationUpdateListener) {
Log.d("DEBUG", "GPS start");
if (firstFixListener != null)
addFirstFixListener(firstFixListener);
if (locationUpdateListener != null)
addLocationUpdateListener(locationUpdateListener);
startLocationListener();
}
/**
* Function to get latitude
* */
public double getLatitude() {
if (location != null) {
latitude = location.getLatitude();
} else {
Log.e("GPSTracker", "getLatitude location is null");
}
// return latitude
return latitude;
}
/**
* Function to get longitude
* */
public double getLongitude() {
if (location != null) {
longitude = location.getLongitude();
} else {
Log.e("GPSTracker", "getLongitude location is null");
}
// return longitude
return longitude;
}
/**
* Function to check GPS/wifi enabled
*
* #return boolean
* */
public boolean canGetLocation() {
return this.canGetLocation;
}
/**
* Function to show settings alert dialog On pressing Settings button will
* lauch Settings Options
* */
public void showSettingsAlert() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
// Setting Dialog Title
alertDialog.setTitle("GPS settings");
// Setting Dialog Message
alertDialog
.setMessage("GPS is not enabled. Do you want to go to settings menu?");
// On pressing Settings button
alertDialog.setPositiveButton("Settings",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
#Override
public void onLocationChanged(Location location) {
if ( location == null)
return;
this.location = location;
mLastLocationMillis = SystemClock.elapsedRealtime();
canGetLocation = true;
if (isGPSFix) {
if (locationUpdateListeners != null) {
for (LocationUpdateListener listener : locationUpdateListeners) {
listener.onLocationChanged(location);
}
}
}
}
#Override
public void onProviderDisabled(String provider) {
canGetLocation = false;
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
private boolean wasGPSFix = false;
// http://stackoverflow.com/questions/2021176/how-can-i-check-the-current-status-of-the-gps-receiver
// answer from soundmaven
private class GPSFixListener implements GpsStatus.Listener {
public void onGpsStatusChanged(int event) {
switch (event) {
case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;
if (isGPSFix != wasGPSFix) { // only notify on changes
wasGPSFix = isGPSFix;
for (FirstFixListener listener : firstFixListeners) {
listener.onFirsFixChanged(isGPSFix);
}
}
break;
case GpsStatus.GPS_EVENT_FIRST_FIX:
// Do something.
break;
}
}
}
}
I have a service that needs to know the phone's location. The main activities of the service are carried out in a thread as follows (with processing stuff removed:
Semaphore locationAcquired = new Semaphore(1);
LocationFinder finder;
...
public void run() {
delaySeconds = 60;
Looper.prepare();
while (true) {
try {
finder.StartFinder();
locationAcquired.acquire();
// do some stuff...
} catch (InterruptedException e) {
if (isDestroy) {
Log.d(TAG, "Closing Monitor Thread");
break;
} // else just wake up and process the location
} catch (Exception e) {
e.printStackTrace();
}
} // end while
} // end run
The LocationFinder class implementation (again, slightly simplified):
package com.ksdagile.opengate;
import...
public class LocationFinder {
public static final int ONE_SECOND = 1000;
LocationListener locationListener;
String provider = LocationManager.PASSIVE_PROVIDER; // passive by default
LocationManager locationManager;
public Location currentLocation;
long updateSeconds;
private boolean isLooking = false;
OpenGateService openGateService;
public LocationFinder(LocationManager _lm, OpenGateService _openGateService) {
openGateService = _openGateService;
locationManager = _lm;
// initialize with whatever location might be available
currentLocation = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
// A new location update is received. Do something useful with it. In this case,
// we're sending the update to a handler which then updates the UI with the new
// location.
currentLocation = location;
String newLoc = String.format("Found new Location lat:%.2f long:%.2f", currentLocation.getLatitude(), currentLocation.getLongitude());
Log.d(getClass().getName(), newLoc);
openGateService.locationAcquired.release();
}
// simple implementations of onProvider<> etc.
};
}
public void SetProvider(boolean isActive) {
if (isActive)
provider = LocationManager.GPS_PROVIDER;
else
provider = LocationManager.PASSIVE_PROVIDER;
}
public void SetFrequency(long delay) {
updateSeconds = delay;
}
public void StartFinder() {
if (!isLooking) {
isLooking = true;
locationManager.requestLocationUpdates(provider, updateSeconds*ONE_SECOND, 10, locationListener);
Log.d(getClass().getName(), String.format("Request location from %s provider, every %d sec.", provider, updateSeconds));
} else
Log.d(getClass().getName(), "Location request running");
}
public void StopFinder() {
locationManager.removeUpdates(locationListener);
isLooking = false;
}
public boolean IsLocating() {
return isLooking;
}
}
My problem is that the onLocationChanged routine is not called, even though I know there are new readings. For example, when configured to read in Passive Mode, I run Waze and see myself moving. Is it possible that the call to onLocationChanged being blocked by the semaphore? If so, how do I get around this? I want to be able to change the parameters for requestLocationUpdate dynamically.
I think getting a tumbleweed badge is a very dubious honor, but here is the answer:
Yes, the .acquire() call blocks the thread, and the onLocationChanged() method is not called.
The solution is to have a handler run a runnable, such as:
public void onLocationChanged(Location location) {
xxxService.locHandler.post(xxxService.locationRun);
}
where
Runnable locationRun = new Runnable() {
#Override
public void run() {
// handle stuff for new location
}
};
The general principle is that you send and handle events, rather than running a polling loop. If you need to handle a non-event, e.g. no new location read, then you set up a countdown timer.
I want to do something like this:
When my application starts I want to start a Service which should check my location
When the application goes to background I want to stop the service
I have two major problems:
How can I detect that my application goes to background? I haver several activities, and I tried that in my MainActivity overriding onPause, but the onPause is also called when I start an other activity.
This problem is more important: How should my Service which checks for my location look like? I tried several approaches, but no success.
My Service looks like this, and it's not working. What should I change to make it work?
package com.pivoscore.service;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
public class LocationService extends Service {
private LocationListener locationListener;
#Override
public IBinder onBind(final Intent intent) {
return null;
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
super.onStartCommand(intent, flags, startId);
return Service.START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
final LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
this.locationListener = new MyLocationListener();
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 100, 0, this.locationListener);
}
private static class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(final Location location) {
}
#Override
public void onProviderDisabled(final String provider) {
}
#Override
public void onProviderEnabled(final String provider) {
}
#Override
public void onStatusChanged(final String provider, final int status, final Bundle extras) {
}
}
}
This Will Help exaclty what your requirment is
but don't forget to Add the permission i've added in comment
and also don't forget to added service tag in manifest File
Code Snipet is Following
public class LocationService extends Service {
public static final String BROADCAST_ACTION = "Hello World";
private static final int TWO_MINUTES = 1000 * 60 * 2;
public LocationManager locationManager;
public MyLocationListener listener;
public Location previousBestLocation = null;
Intent intent;
int counter = 0;
#Override
public void onCreate() {
super.onCreate();
intent = new Intent(BROADCAST_ACTION);
}
#Override
public void onStart(Intent intent, int startId) {
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
listener = new MyLocationListener();
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, listener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
protected boolean isBetterLocation(Location location, 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
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
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
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
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;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
#Override
public void onDestroy() {
// handler.removeCallbacks(sendUpdatesToUI);
super.onDestroy();
Log.v("STOP_SERVICE", "DONE");
locationManager.removeUpdates(listener);
}
public static Thread performOnBackgroundThread(final Runnable runnable) {
final Thread t = new Thread() {
#Override
public void run() {
try {
runnable.run();
} finally {
}
}
};
t.start();
return t;
}
public class MyLocationListener implements LocationListener
{
public void onLocationChanged(final Location loc)
{
Log.i("**************************************", "Location changed");
if(isBetterLocation(loc, previousBestLocation)) {
loc.getLatitude();
loc.getLongitude();
intent.putExtra("Latitude", loc.getLatitude());
intent.putExtra("Longitude", loc.getLongitude());
intent.putExtra("Provider", loc.getProvider());
sendBroadcast(intent);
/*final Geocoder geocoder = new Geocoder(getApplicationContext(), Locale.getDefault());
String Text = "";
try {
List<Address> addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);
Text = "My current location is: "+addresses.get(0).getAddressLine(0);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Text = "My current location is: " +"Latitude = " + loc.getLatitude() + ", Longitude = " + loc.getLongitude();
}
*/
//Toast.makeText( getApplicationContext(), "Location polled to server", Toast.LENGTH_SHORT).show();
}
}
public void onProviderDisabled(String provider)
{
Toast.makeText( getApplicationContext(), "Gps Disabled", Toast.LENGTH_SHORT ).show();
}
public void onProviderEnabled(String provider)
{
Toast.makeText( getApplicationContext(), "Gps Enabled", Toast.LENGTH_SHORT).show();
}
public void onStatusChanged(String provider, int status, Bundle extras)
{
}
}
Application is not a visual component in Android. It is divided into Activities, each of them run when visible, paused and destroyed otherwise. So, there is no concept of whole Application going to background, Activities are paused and resumed on the basis of their individual visibility and are completely independent of other Activities in this matter.
Your Service shall register with Location Manager in its onCreate(), unregister from the same in its onDestroy(). In its onBind() it shall return a Messenger object. And, in onLocationChanged() it should send a message through its shared Messenger. No need to use START_STICKY as you don't want Service running all the time.
The Activity (can be any activity in the App) just needs to call bindService() in its onStart(), The service will start if not already, and Activity will get a Messenger from service. Also, Activity should call unbindService() from its onStop(). The Service will automatically stop when nothing is bound to it.
If you need to do the stuff in point 3 at App (Task) level, implement the Application class, and use its onCreate() and onTerminate(). Application class is not paused or stopped like an Activity.
I suggest you to use the latest location api provided by google(also compatible to version 2.2).
here is a example shows how to use that:
https://github.com/chenjishi/android_location_demo
when your app run to background, you can call disconnect to stop the location update.
Try This code.. By using this you can find whether your application is in foreground or background. Hope this will help you.
try {
foreground = new ForegroundCheckTask().execute(ctx).get();
}
======================================
class ForegroundCheckTask extends AsyncTask<Context, Void, Boolean> {
#Override
protected Boolean doInBackground(Context... params) {
final Context context = params[0].getApplicationContext();
return isAppOnForeground(context);
}
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
String activePackageName = activityManager.getRunningTasks(1).get(0).topActivity.getPackageName();
if (activePackageName.equals(packageName)) {
return true;
}
else{
return false;
}
}
}
I have a fairly straightforward piece of code, which is is a Service that runs periodically, records the current location (using network provider), sends it to the server, then goes back to sleep.
I am testing this on two different phones - an SGS2 with a regular monthly plan and a cheapo ZTE with a prepaid SIM card (has data, but minutes are 10c/min). I've found that when I take both phones and go for a drive, the SGS2 works perfectly fine, but ZTE seems to lose the ability to get a fix.
The ZTE wakes up, sets up the listener, gets a location fix, however the location points to my house (where it got the last wifi-based fix), not the true current location. The timestamp of the location is up to date, so when I receive a location update, I really can't tell whether the location is valid (as in the SGS2, or when the ZTE is at home) or bunk (such as when I'm driving with the ZTE).
Has anyone seem similar problems before? Does it have anything to do with the prepaid card, or the ZTE phone itself? Unfortunately, I can't swap the SIM cards (I would have to root/unlock the phones), so I can't test that out.
I've included the code below, but since it works fine on the SGS2, I don't think there's much of a problem.
public class LocationRecorder extends Service {
private volatile Location lastLocation;
private LocationManager locationManager;
private LocationListener locationListener;
private static volatile PowerManager.WakeLock wakeLock = null;
private static synchronized PowerManager.WakeLock getWakeLock(Context context) {
if (wakeLock == null) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "LocationRecorder");
wakeLock.acquire();
}
return wakeLock;
}
public static void startLocationRecorder(Context context, Intent service) {
getWakeLock(context);
context.startService(service);
}
#Override
public void onCreate() {
Log.d("LocationRecorder", "Starting Location Service");
locationManager = ((LocationManager)getSystemService(LOCATION_SERVICE));
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Log.d("Location Changed", location.toString());
if (location.getExtras()!=null) {
String x = "";
for (String key : location.getExtras().keySet()) {
x+=key+":"+location.getExtras().get(key).toString()+", ";
}
Log.d("Location Changed Extras", x);
}
setLocation(location);
}
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.d("Status Changed", provider+" "+status);
if (extras!=null) {
String x = "";
for (String key : extras.keySet()) {
x+=key+":"+extras.get(key).toString()+", ";
}
Log.d("Status Changed Extras", x);
}
}
public void onProviderEnabled(String provider) {
Log.d("Provider Enabled", provider);
}
public void onProviderDisabled(String provider) {
Log.d("Provider Disabled", provider);
}
};
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
waitForLocation();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_REDELIVER_INTENT;
}
#Override
public void onDestroy() {
locationManager.removeUpdates(locationListener);
try {
wakeLock.release();
wakeLock = null;
}
catch (Exception e) {
wakeLock = null;
}
Log.d("LocationRecorder", "Destroying service");
super.onDestroy();
}
protected void waitForLocation() {
new Thread() {
#Override
public void run() {
setLocation(null);
for (int i=0; i<3;i++) {
Log.d("LocationRecorder", "Waiting for location");
try {Thread.sleep(10000);} catch(Exception e) {};
if (getLocation() != null) {
Log.d("LocationRecorder", "Sending new location!");
new Utilities(LocationRecorder.this).updateLocation(getLocation().getLatitude(),
getLocation().getLongitude(), getLocation().getAccuracy());
break;
}
}
stopSelf();
}
}.start();
}
public synchronized void setLocation(Location newLocation) {
lastLocation = newLocation;
}
public synchronized Location getLocation() {
return lastLocation;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
This is expected behaviour. You may just need to wait longer before you destroy the location manager to get a more up to date location.
Here's a better description from Google Developer Docs.