Fused location provider doesn't seem to use GPS receiver

63,713

Solution 1

I solved it. The problem was that "Let Google apps access your location" was turned off: enter image description here

When I turn it on I get GPS readings and when it's off I don't.

I had left it off for two reasons:

  1. I'm developing an app to be used to lots of devices at a company and I want minimum manual configuration to be necessary

  2. The screen says clearly "This setting affects Google apps only." I know that Play Services is Google software but I didn't think Google would expect an end user to understand that.

Then I got the Android 4.4.2 update and the location settings page has changed. It appears that I can have Google Location Reporting turned off and still get GPS readings from the fused location provider: enter image description here

So maybe Google realised that the setting was confusing and improved it. Either way, I'd have saved a lot of time if I'd got 4.4.2 a few days ago.

Solution 2

The problem is with getLastLocation() because it uses a cached location. I had the same problem as I also tried to use this simple approach. Since, I have switched to listening to updates (and stopping after 1st successfull update automatically).

This is my code that works.

First, the check for availability in Application (not essential, can be in Activity and without keeping of result):

public class MainApp extends Application {
  public static enum PlayServices {
    NOT_CHECKED, AVAILABLE, UNAVAILABLE
  };
  public static PlayServices mPlayServices = PlayServices.NOT_CHECKED;

  @Override
  public void onCreate() {
    super.onCreate();

    if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) {
      MainApp.mPlayServices = MainApp.PlayServices.AVAILABLE;
    }
  }
}

Then, on to the Activity:

public class MainActivity extends SherlockFragmentActivity implements
  GooglePlayServicesClient.ConnectionCallbacks,
  GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {

In its onCreate():

if (MainApp.mPlayServices != MainApp.PlayServices.UNAVAILABLE) {
  mLocationClient = new LocationClient(this, this, this);

  mLocationRequest = LocationRequest.create();
  mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
  mLocationRequest.setInterval(5000);
  mLocationRequest.setNumUpdates(1);
  mLocationRequest.setFastestInterval(1000);

  mUpdatesRequested = false;
  MainApp.prefs.edit().putBoolean(MainApp.KEY_LOCATION_UPDATES_REQUESTED, mUpdatesRequested)
      .commit();
}

The rest of the MainActivity class:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  Log.d(this.getClass().getSimpleName(), "onActivityResult(" + requestCode + ", " + resultCode
      + ")");
  // Decide what to do based on the original request code
  switch (requestCode) {
    case MainApp.PLAY_CONNECTION_FAILURE_RESOLUTION_REQUEST:
      /*
       * If the result code is Activity.RESULT_OK, try
       * to connect again
       */
      switch (resultCode) {
        case Activity.RESULT_OK:
          // here we want to initiate location requests!
          mLocationClient = new LocationClient(this, this, this);

          break;
      }
      break;
  }
}

@Override
public void onConnected(Bundle dataBundle) {
  Log.d(this.getClass().getSimpleName(), "onConnected()");

  Log.d(this.getClass().getSimpleName(), "Google Play Services are available.");
  MainApp.mPlayServices = MainApp.PlayServices.AVAILABLE;

  if (!mUpdatesRequested) {

    LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

    boolean gps_enabled = false;
    try {
      gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    } catch (Exception ex) {
    }

    boolean network_enabled = false;
    try {
      network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    } catch (Exception ex) {
    }

    // don't start listeners if no provider is enabled
    MainApp.locEnabled = gps_enabled || network_enabled;

    if (!MainApp.locEnabled) {
      // we have access to PlayServices, but user has disabled location visibility --> alert him
      alertLocationOff();
    } else {
      mLocationClient.requestLocationUpdates(mLocationRequest, this);
      mUpdatesRequested = true;
    }
  }
}

@Override
public void onDisconnected() {
  Log.d(this.getClass().getSimpleName(), "onDisconnected()");
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
  Log.d(this.getClass().getSimpleName(), "onConnectionFailed()");

  Log.d(this.getClass().getSimpleName(), "Google Play Services not available.");
  MainApp.mPlayServices = MainApp.PlayServices.UNAVAILABLE;

  /*
   * Google Play services can resolve some errors it detects.
   * If the error has a resolution, try sending an Intent to
   * start a Google Play services activity that can resolve
   * error.
   */
  if (connectionResult.hasResolution()) {
    try {
      // Start an Activity that tries to resolve the error
      connectionResult.startResolutionForResult(this,
          MainApp.PLAY_CONNECTION_FAILURE_RESOLUTION_REQUEST);
      /*
       * Thrown if Google Play services canceled the original
       * PendingIntent
       */
    } catch (IntentSender.SendIntentException e) {
      // Log the error
      e.printStackTrace();
    }
  } else {
    /*
     * If no resolution is available, display a dialog to the
     * user with the error.
     */
    GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();
  }
}

@SuppressLint("NewApi")
@Override
public void onLocationChanged(Location location) {
  Log.d(this.getClass().getSimpleName(), "onLocationChanged(), location=" + location);

  if (location != null) {
    boolean present = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
      present = Geocoder.isPresent();
    }

    if (present) {
      (new ExtractLocationTask(this)).execute(location);
    } else {
      Log.e(this.getClass().getSimpleName(), "Geocoder not present");
      MainApp.mPlayServices = MainApp.PlayServices.UNAVAILABLE;
    }
  }
}


private class ExtractLocationTask extends AsyncTask<Location, Void, Boolean> {
  Context mContext;

  public ExtractLocationTask(Context context) {
    super();
    mContext = context;
  }

  @Override
  protected Boolean doInBackground(Location... params) {
    Log.d(getClass().getSimpleName(), "ExtractLocationTask.onPreExecute()");

    boolean found = false;
    try {
      Geocoder geoCoder_local = new Geocoder(mContext, Locale.getDefault());
      Geocoder geoCoder_en = new Geocoder(mContext, Locale.ENGLISH);

      List<Address> addresses_local = geoCoder_local.getFromLocation(params[0].getLatitude(),
          params[0].getLongitude(), 10);
      List<Address> addresses_en = geoCoder_en.getFromLocation(params[0].getLatitude(),
          params[0].getLongitude(), 10);

      if (addresses_local != null && addresses_local.size() > 0) {

        // do what you want with location info here

        // based on mLocationRequest.setNumUpdates(1), no need to call
        // removeLocationUpdates()

        MainApp.locEnabled = true;

        mUpdatesRequested = false;
        MainApp.prefs.edit()
            .putBoolean(MainApp.KEY_LOCATION_UPDATES_REQUESTED, mUpdatesRequested).commit();

        found = true;
      }
    } catch (IOException e) {
      Log.e(this.getClass().getSimpleName(), "Exception: ", e);
    }

    return found;
  }

  @Override
  protected void onPostExecute(Boolean found) {
    Log.d(getClass().getSimpleName(), "ExtractLocationTask.onPostExecute()");

    if (found) {
      // update UI etc.
    } else if (!mUpdatesReRequested) {
      mLocationClient.requestLocationUpdates(mLocationRequest, (LocationListener) mContext);
      mUpdatesRequested = true;
      mUpdatesReRequested = true;
    }
  }
}

I hope this will help you get it to work!

Solution 3

Location provider won't wake up GPS until some of clients ask (subscribe) for high precision location (as described in examples given by other users). GPS test app doesn't use location provider but uses old "direct" way of obtaining location.

Also there is expiry mechanism, which removes information about last location after some time if it is believed to be stale.

Summing up all above it is really possible that LP(Location Provider) has nothing to give you.

Solution 4

I think your are testing your application in Indoor , it doesn't works..

and code flow..

public void setUpLocationClientIfNeeded() {
        createLocReq();
        if (mLocationClient == null) {
            mLocationClient = new LocationClient(getApplicationContext(), this, // ConnectionCallbacks
                    this); // OnConnectionFailedListener
        }
    }

    private void createLocReq() {
        if (mLocationRequest == null) {
            // Create a new global location parameters object
            mLocationRequest = LocationRequest.create();
            // Set the update interval
            mLocationRequest.setInterval(LocationServices.UPDATE_INTERVAL_IN_MILLISECONDS);
            // Use high accuracy
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            // Set the interval ceiling to one minute
            mLocationRequest.setFastestInterval(LocationServices.FAST_INTERVAL_CEILING_IN_MILLISECONDS);

            mLocationClient = new LocationClient(getApplicationContext(), this, this);
            mLocationClient.connect();
        }
    }

    public void updateLocation(Location location) {
        if (lastTrackedLat == 0.0) {
            lastTrackedLat = location.getLatitude();
        }
        if (lastTrackedLng == 0.0) {
            lastTrackedLng = location.getLongitude();
        }

        currentLat = location.getLatitude();
        currentLng = location.getLongitude();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location != null) {
            this.location = location;
            updateLocation(location);
        }
    }

    public Location getLocation() {
        // mLocationClient.getLastLocation();
        return location;
    }

Solution 5

All you need to do is to add a priority property to the request object like this.

public void onConnected(Bundle arg0)
{
    locationrequest = LocationRequest.create();
    locationrequest.setInterval(1000);
    locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationclient.requestLocationUpdates(locationrequest, this);
}

Maybe use a boolean variable to let the user have options to force GPS like

boolean forceGPS=false;
.
.
.//make the user choose to change the value of the boolean
.
.
public void onConnected(Bundle arg0)
    {
        locationrequest = LocationRequest.create();
        locationrequest.setInterval(1000);
        if(forceGPS==true)
        {
        locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        }
        locationclient.requestLocationUpdates(locationrequest, this);
    }
Share:
63,713
cja
Author by

cja

Updated on April 07, 2020

Comments

  • cja
    cja about 4 years

    Android 4.3 on Moto G, Android 4.4.2 on Nexus 7 2012, Android 4.4.2 on Nexus 5. Android Studio 0.4.

    I don't want to receive regular location updates, I just want an accurate location when the user presses a button.

    I have followed this example: https://developer.android.com/training/location/retrieve-current.html

    In manifest file:

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    

    I check that Play Services are available using GooglePlayServicesUtil.isGooglePlayServicesAvailable

    In main activity:

    //in activity onCreate method
    mLocationClient = new LocationClient(this, this, this);
    
    @Override
    protected void onStart() {
        mLocationClient.connect();
        super.onStart();
    }
    
    @Override
    protected void onStop() {
        mLocationClient.disconnect();
        super.onStop();
    }
    
    //in button onclick method    
    mCurrentLocation = mLocationClient.getLastLocation();
    

    I have no SIM card. If I enable Wifi then sometimes I get an accurate location. Other times mCurrentLocation is null.

    If I disable Wifi then mCurrentLocation is always null.

    I am testing outside in several locations always with a clear view of the sky. I waited three minutes in each location.

    I never see the GPS icon appear on the Android notification bar at the top of the screen.

    I have these location settings: enter image description here

    A GPS Test app manages to use GPS successfully indoors on the same device with Wi-Fi disabled so GPS is working: enter image description here

    Registering for location updates, as at https://developer.android.com/training/location/receive-location-updates.html, doesn't work either. Registered method never called.

    What am I doing wrong?