Android find GPS location once, show loading dialog
Asked Answered
K

3

26

I am writing an app that requires the user's current location (lastknownlocation won't be very helpful) and displays a list of all the closest "items" to them taken from the database.

I have got the finding of the closest items working well but only using a hardcoded latitude and longitude location for the time being but now it is time to implement finding the actual location.

Can anyone provide an example of how to have the app find the current fine location in the background and have the app wait. I only need the location once not updates as the person moves. I have implemented a location listener however I understand getting a GPS fix can be slow. I have seen other examples using last known location or implementing the location listener which continues to run and update however as my Activity needs the location coordinates before it can display anything the app just crashes. I need to show a Progress Dialog while its searching.

How can I have the locationlistener run once in the background and then remove location updates once it finds the location. I need the rest of the application to wait until it has a GPS location or timeout after say 20-30seconds. I would like a ProgressDialog up so the user knows that something is going on, but it just has to be the spinning loading animation not a percentage or anything. If possible I would like the user to be able to cancel the dialog if they are sick of waiting which then they can search by typing suburb etc instead.

I have been trying to do it with threads but it is getting way more complicated than I feel it should be and still not working anyway. On iPhone this is much more simple?

Can anyone provide a nice way of doing this, I have been ripping my hair out for a week on this and it is really putting me behind schedule for the rest of the app completion date.

In summary I want to:
* Find current coordinates
* Show Progress Dialog while getting location
* Have app be able to wait for successful location or give up after a certain time?
* If Successful: Dismiss Progress Dialog and use coordinates to run my other methods for finding items closeby.
* If Failed to get location: Dismiss Progress Dialog and show error message, and encourage user to use menu to go to Search Activity.
* Use these coordinates if the user chooses map view from the menu, to show current location on the map and pins dropped at all the nearby items by using their coordinates from the database.

Thanks guys, I hope I have explained myself well enough. Any questions just ask, I will be checking back regularly as I am keen to get this part completed. Cheers

EDIT
Code as requested

locationList activity file

protected void onStart()
{
    super.onStart();

    // ....

    new LocationControl().execute(this);
}


private class LocationControl extends AsyncTask<Context, Void, Void>
{
    private final ProgressDialog dialog = new ProgressDialog(branchAtmList.this);

    protected void onPreExecute()
    {
        this.dialog.setMessage("Determining your location...");
        this.dialog.show();
    }

    protected Void doInBackground(Context... params)
    {
        LocationHelper location = new LocationHelper();

        location.getLocation(params[0], locationResult);

        return null;
    }

    protected void onPostExecute(final Void unused)
    {
        if(this.dialog.isShowing())
        {
            this.dialog.dismiss();
        }

        useLocation(); //does the stuff that requires current location
    }

}

public LocationResult locationResult = new LocationResult()
{
    @Override
    public void gotLocation(final Location location)
    {
        currentLocation = location;
    }
};  

LocationHelper class

    package org.xxx.xxx.utils;

import java.util.Timer;
/*
imports
*/

public class LocationHelper
{
    LocationManager locationManager;
    private LocationResult locationResult;
    boolean gpsEnabled = false;
    boolean networkEnabled = false;

    public boolean getLocation(Context context, LocationResult result)
    {       
        locationResult = result;

        if(locationManager == null)
        {
            locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
        }
            //exceptions thrown if provider not enabled
            try
            {
                gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            }
            catch (Exception ex) {}
            try
            {
                networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            }
            catch (Exception ex) {}

            //dont start listeners if no provider is enabled
            if(!gpsEnabled && !networkEnabled)
            {
                return false;
            }

            if(gpsEnabled)
            {
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
            }
            if(networkEnabled)
            {
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
            }


            GetLastLocation();
            return true;
    }

    LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location)
        {
            locationResult.gotLocation(location);
            locationManager.removeUpdates(this);
            locationManager.removeUpdates(locationListenerNetwork);

        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extra) {}
    };

    LocationListener locationListenerNetwork = new LocationListener() {
        public void onLocationChanged(Location location)
        {
            locationResult.gotLocation(location);
            locationManager.removeUpdates(this);
            locationManager.removeUpdates(locationListenerGps);

        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extra) {}

    };

    private void GetLastLocation()
    {
            locationManager.removeUpdates(locationListenerGps);
            locationManager.removeUpdates(locationListenerNetwork);

            Location gpsLocation = null;
            Location networkLocation = null;

            if(gpsEnabled)
            {
                gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            }
            if(networkEnabled)
            {
                networkLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            }

            //if there are both values use the latest one
            if(gpsLocation != null && networkLocation != null)
            {
                if(gpsLocation.getTime() > networkLocation.getTime())
                {
                    locationResult.gotLocation(gpsLocation);
                }
                else
                {
                    locationResult.gotLocation(networkLocation);
                }

                return;
            }

            if(gpsLocation != null)
            {
                locationResult.gotLocation(gpsLocation);
                return;
            }

            if(networkLocation != null)
            {
                locationResult.gotLocation(networkLocation);
                return;
            }

            locationResult.gotLocation(null);
    }

    public static abstract class LocationResult
    {
        public abstract void gotLocation(Location location);
    }
}

This really seems like a big effort just to get the current location once? I do not require updates or anything? Is there not a simpler way of having the application wait for a result? I have been stuck on this for so long.

Kerrykersey answered 15/8, 2010 at 3:15 Comment(1)
#3145589Roybn
M
16

Use an AsyncTask and use both network_provider as well as gps_provider, which means two listeners. gps takes longer to get a fix, maybe sometimes a minute and only works outdoors, while network gets a location pretty fast.

A good code example is here: What is the simplest and most robust way to get the user's current location on Android?

For AsyncTask, look http://developer.android.com/reference/android/os/AsyncTask.html

There are also many code example here on SO for it, for example here: Android Show a dialog until a thread is done and then continue with the program

EDIT: code concept:

add class variable

boolean hasLocation = false;

call in onCreate (not in AsyncTask):

locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

then in locationListener.onLocationChanged, change the value once a location is found:

boolean hasLocation = false;

AsyncTask: leave as it is, but don't call

LocationHelper location = new LocationHelper();
location.getLocation(params[0], locationResult);

there, instead do

Long t = Calendar.getInstance().getTimeInMillies(); 
while (!hasLocation && Calendar.getInstance().getTimeInMillies()-t<30000)) {
    Thread.Sleep(1000); 
};    

probably with a delay in between would be sufficient.

Moraine answered 15/8, 2010 at 4:53 Comment(13)
Thanks this looks helpful. I thought GPS API stuff was asynchronous anyway? Or do I still need to implement this in order to have the app wait for a result? Based on those examples: So would I call myLocation.getLocation(this, locationResult) from within doInBackground(final String... args)? I just don't really understand when onPostExecute(final Void unused) would be called as the end result of the MyLocation stuff is gotLocation(final Location location)? also what happens if the timer runs out instead?Kerrykersey
yes the gps stuff is async, but showing a progress isn't. therefore you need to wrap it in an AsyncTask, since the progress dialog depends on your gps status/results. onPostExecute and onPreExecute run on the UI main thread, therefore you update your UI in there, i.e. once you got a gps fix for example, and also dismiss the progress dialog in there.Moraine
Ok thanks I will give this a go. How does onPostExecute know when I have a gps fix or when it is finished executing? ie how does it know when either gotLocation() is called or the timer runs out?Kerrykersey
for a GPS fix (not network) you can use a GpsStatus.Listener, although I experienced some issue with it, but maybe it's because I was indoor... #3287889Moraine
Have tried using AsyncTask today and am having problems. from doInBackground I call LocationHelper location = new LocationHelper(); and location.getLocation(params[0], locationResult); When this gets to the line: locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps); It throws an exception: ERROR/AndroidRuntime(6534): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() Help please?Kerrykersey
please post all relevant parts of the code somewhere, pastebin or in the initial question, to take a look. Not sure why you'd need Looper.prepare - are you running a thread somewhere beside the AsyncTask? You could add that line (Looper.prepare()) and see if it works. But let's see the code first.Moraine
I asssume some thread stuff is automatically happening in the requestLocationUpdates which is where the exception occurs. This seems like a very complex way of doing what I want? Does it have to be this hard?Kerrykersey
don't call the requestLocationUpdates from within the AsyncTask/background. See the code concept I posted above (call it onCreate or onResume) - this way should work. Yes, you could probably simplify some code - refer to my code concept above.Moraine
Ok I'll give that a go. One question, if I'm replacing LocationHelper call with the while loop when do I call location.getLocation()?Kerrykersey
Ignore my last question I have got it working now. How would I limit the search for location to say 30 seconds then timeout? Otherwise the progress dialog is going to spin forever.Kerrykersey
see edited code above: Long t = Calendar.getInstance().getTimeInMillies(); while (!hasLocation || Calendar.getInstance().getTimeInMillies()-t<30000)) {};Moraine
Working good for me so far, thanks! Not quite sure how long to keep my timeout at as getting a gps or network location seems so fickle on my device but I'll keep testing values til I'm happy. Marked as Accepted. :)Kerrykersey
Thank you, i managed to get it working! thx Mathias and Daniel :DDara
K
12

I still don't like how this works at all and it seems way more complicated than it should be but until someone provides a better way I have it working for now...

If someone comes across this and suggest a much better cleaner result to just get the user's current location coordinates just once then stop looking that would be greatly appreciated!!

private LocationControl locationControlTask;
private boolean hasLocation = false;
LocationHelper locHelper;

From onCreate() I call

locHelper = new LocationHelper();
locHelper.getLocation(context, locationResult);
locationControlTask = new LocationControl();
locationControlTask.execute(this);

I then have a a private class LocationControl

private class LocationControl extends AsyncTask<Context, Void, Void>
    {
        private final ProgressDialog dialog = new ProgressDialog(activityname.this);

        protected void onPreExecute()
        {
            this.dialog.setMessage("Searching");
            this.dialog.show();
        }

        protected Void doInBackground(Context... params)
        {
            //Wait 10 seconds to see if we can get a location from either network or GPS, otherwise stop
            Long t = Calendar.getInstance().getTimeInMillis();
            while (!hasLocation && Calendar.getInstance().getTimeInMillis() - t < 15000) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            return null;
        }

        protected void onPostExecute(final Void unused)
        {
            if(this.dialog.isShowing())
            {
                this.dialog.dismiss();
            }

            if (currentLocation != null)
            {
                useLocation();
            }
            else
            {
                //Couldn't find location, do something like show an alert dialog
            }
        }
    }

Then a locationResult..

public LocationResult locationResult = new LocationResult()
    {
        @Override
        public void gotLocation(final Location location)
        {
            currentLocation = new Location(location);
            hasLocation = true;
        }
    };

Also to avoid you pulling your hair out like I did for ages remember to stop the locationcontrol task if someone leaves the activity early and also stop location updates to stop the GPS from running when the user leaves. In onStop() do something like this:

locHelper.stopLocationUpdates();
locationControlTask.cancel(true);

Also stopLocationUpdates in the Location Helper class does the following:

public void stopLocationUpdates()
{
locationManager.removeUpdates(locationListenerGps);
locationManager.removeUpdates(locationListenerNetwork);
}

Good luck with it, I needed it...

Kerrykersey answered 3/11, 2010 at 5:1 Comment(1)
posting complete working code(i.e github) would be awesome for you and for us as well :)Ryun
P
0

Instead of creating an AsyncTask why didn't you create a ProgressDialog box and dismiss it on public void gotLocation(final Location location){}? You can also add a onCancelListener to the ProgressDialog box and cancel any current location requests.

Pastille answered 5/4, 2011 at 14:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.