With the more recent version of Android... you are supposed to check if the user has given you permission before you use their location info. I\'ve gone through the android
Also, checking to see if you have the permissions and actually requesting it are two different things.
As the android developer blog suggests you need to ask for permissions in a way like this:
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_RESULT_CODE);
And then handle the result of the request by overriding onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
.
I was facing the same error as you. To save battery, i wanted to use the lastKnownLocation instead of polling all the time. Sometimes, lastKnownLocation was null, no matter what i checked before.
If you have a look in the documentation, it is said that:
The getLastLocation() method returns a Location object from which you can retrieve the latitude and longitude coordinates of a geographic location. The location object returned may be null in rare cases when the location is not available.
To fix this, i implemented a combination of using the lastKnownLocation, and polling for a new position as a fallback.
My basic structure here is
This approach works like a charm. I have'nt managed to figure out a better solution so far, but i would suggest you to try it out.
HERE WAS THE PROBLEM: Android Emulator
Scroll down to "Working with the Extended Controls, Settings, and Help".
Long story short: IF YOU ARE USING AN EMULATOR, YOU HAVE TO FAKE YOUR LOCATION DATA!!!!! The emulator doesn't support wifi (or as far as I can tell, GPS data either).
Click on your "phone" (your emulator) to make sure it's selected, and press ctrl+shift+L Type in any location you want and click "Send" and viola... you'll have a location. I knew something was up when GoogleMaps on my emulator wasn't even working. I would search for directions and it would say "Waiting for Location" and nothing would happen.
Good freaking riddance.
After checking the permissions with ContextCompat.checkSelfPermission
, if it's not granted, you can request for it. From the documentation:
Also, you need to deal with connection failures when using GoogleApiClient
(documentation). In your code, the connection was failing and you weren't managing it on onConnectionFailed
.
And finally, I have removed the following from your code (this was causing a connect failure and are not neccesary to use LocationServices
and get the last location):
.addApi(Drive.API)
.addApi(Plus.API)
.addScope(Drive.SCOPE_FILE)
Here is a working example:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static final int FINE_LOCATION_PERMISSION_REQUEST = 1;
private static final int CONNECTION_RESOLUTION_REQUEST = 2;
private GoogleApiClient mGoogleApiClient;
private GoogleMap mMap;
private Location mLastLocation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
buildGoogleAPIClient();
}
@Override
protected void onResume() {
super.onResume();
buildGoogleAPIClient();
}
private void buildGoogleAPIClient() {
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
@Override
public void onConnected(@Nullable Bundle bundle) {
findLocation();
}
protected void onStart() {
mGoogleApiClient.connect();
super.onStart();
}
protected void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
}
@Override
public void onConnectionSuspended(int i) {
Toast.makeText(this, "Connection suspended", Toast.LENGTH_SHORT).show();
}
@Override
public void onConnectionFailed(@NonNull final ConnectionResult connectionResult) {
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(this, CONNECTION_RESOLUTION_REQUEST);
} catch (IntentSender.SendIntentException e) {
// There was an error with the resolution intent. Try again.
mGoogleApiClient.connect();
}
} else {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 1);
dialog.show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CONNECTION_RESOLUTION_REQUEST && resultCode == RESULT_OK) {
mGoogleApiClient.connect();
}
}
private void findLocation() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
FINE_LOCATION_PERMISSION_REQUEST);
} else {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
LatLng myLat = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(myLat));
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case FINE_LOCATION_PERMISSION_REQUEST: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
findLocation();
}
}
}
}
}
Before you get the last known location you need to check four things:
I recommend to use a state-machine or a bool for each thing and ask for the location only if all four is true. For example:
protected void getLastKnownLocationIfReady(){
if(isGoogleApiConnected && isPermissionGranted && isLocationEnabled && isGoogleMapReady){
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
LatLng myLat = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(myLat));
}
}
call getLastKnownLocationIfReady() function every time you change one of the boolean variables.
so your onConnected
would look like for example:
@Override
public void onConnected(@Nullable Bundle bundle) {
isGoogleApiConnected = true;
getLastKnownLocationIfReady();
}
check permission like:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, FINE_LOCATION_PERMISSION_REQUEST);
} else {
isPermissionGranted = true;
getLastKnownLocationIfReady();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case FINE_LOCATION_PERMISSION_REQUEST: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
isPermissionGranted = true;
getLastKnownLocationIfReady();
}
}
}
}
you can ask for enable the location like:
public void requestLocationAccess(){
if(mLocationRequest == null) createLocationRequest();
final LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true); //this is the key ingredient
com.google.android.gms.common.api.PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>(){
@Override
public void onResult(@NonNull LocationSettingsResult result){
final Status resultStatus = result.getStatus();
switch(resultStatus.getStatusCode()){
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied.
isLocationEnabled = true;
getLastKnownLocationIfReady();
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user a dialog. You need to override OnActivityResult if you want to handle the user's interaction with the dialog.
resultStatus.startResolutionForResult(this, REQUEST_LOCATION);
break;
default:
break;
}
}
});
}
modify your onMapReady
like:
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
isGoogleMapReady = true;
getLastKnownLocationIfReady();
}