2

I have an app which gets the users current location and then loads a Google Map and plots markers of interest in their area. It works flawlessly on everything below Marshmallow. I've added the run-time permissions check, and they are being set as I do get the prompt, and after I hit accept I see the Permission listed in the app details from the settings of the phone. However, I can not for the life of me figure out why I'm getting no location back.

I am using the example as seen here https://developer.android.com/training/location/retrieve-current.html

I have all the permissions set with the tag in the manifest. I even have my Fragment implementing the LocationListener. However, the onLocationChanged method never gets called. I am calling it within the onConnected method of the Google API Client like below..

@Override
    public void onConnected(@Nullable Bundle bundle) {
        try {
            Log.d("MYAPPTAG","isConnected: " + mGoogleApiClient.isConnected());
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
            myLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        } catch (SecurityException ex) {
            myLocation = null;
        }
    }

the onConnected method DOES get called because I get my Log to the console. myLocation is always null though. I get a message in the console everytime I call getLastLocation that says

No Location to return for getLastLocation()
GoogleSignatureVerifier: com.myappname.android signature not valid.  Found: LONG KEY HERE

Is there something special I need to do in Marshmallow?

my OnLocationChanged method

@Override
    public void onLocationChanged(Location location) {
        myLocation = location;
        Log.d("MYAPPTAG", "LocatinChngListner, loc: " + location.getLatitude() + "," + location.getLongitude());     
    }

AndroidManifest.xml - permissions section (above node)

<!--- App permissions -->
    <permission android:name="com.myappname.android.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
    <uses-permission android:name="com.myappname.android.permission.MAPS_RECEIVE"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <permission android:name="com.myappname.android.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="com.myappname.android.permission.C2D_MESSAGE"/>

onCreate Method snippet

createLocationRequest();
        if (mGoogleApiClient == null) {
            mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        }

createLocationRequest method

private void createLocationRequest(){
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(5000);
        mLocationRequest.setFastestInterval(1000);
    }

onStart() onStop() methods

@Override
public void onStart() {
        if (!mGoogleApiClient.isConnected()) mGoogleApiClient.connect();
        super.onStart();
    }

@Override    
public void onStop() {
        if (mGoogleApiClient.isConnected()) mGoogleApiClient.disconnect();
        super.onStop();
    }

I call this code below right after the Fragment onCreate method

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1 && (ContextCompat.checkSelfPermission(getActivity(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
            requestPermissions(new String[] { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }, REQUEST_PERMISSION_USER_LOCATION);
        } else {
            googleMapsLocationPermissionContainer.setVisibility(View.GONE);
            getUserLocationAndInitializeMap();
        }

googleMapsLocationPermissionContainer is just a layout I overlay on the map until the permissions are granted.

getUserLocationAndInitializeMap()

try {
                MapsInitializer.initialize(getActivity());
                // Set reference for map object
                if (map == null) {
                    mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
                    mapFragment.getMapAsync(this);
// Here I set the map to invisible then after I plot all the markers I set it to visible again

                    mapFragment.getView().setVisibility(View.INVISIBLE);
                }
            } catch (Exception e) {
                // Show the google maps alert dialog
                showGoogleMapsErrorDialog();
            }
Phil
  • 4,029
  • 9
  • 62
  • 107
  • Show more code. I am doing an app with location and works perfect on 6.0. My initial guess is that you need the manifest permissions for android 6.0. It is a different thing in Android-23. – Andrei T Jul 28 '16 at 17:53
  • @AndreiT - what are the manifest permissions I need in 23+? – Phil Jul 28 '16 at 17:54
  • Something like that: https://developer.android.com/training/permissions/requesting.html. I also had everything and in Android-6, it just did not work because I was missing the permission calls and so on. – Andrei T Jul 28 '16 at 17:54
  • As far as I know(not much knowledge yet) in Android 6 you can disable and enable permissions for apps. Thus, you need something like before. But before giving an answer put more code please. – Andrei T Jul 28 '16 at 17:55
  • Also add the code you are using for registering for location, GoogleClient or GoogleApi(they always change the freaking name). – Andrei T Jul 28 '16 at 18:01
  • @AndreiT - I've added code...is there anything else you need to see? Any other methods? – Phil Jul 28 '16 at 18:05
  • All good, nothing more is added. It looks it is just the stupid permissions. I will try to respond to you. – Andrei T Jul 28 '16 at 18:09

1 Answers1

1

My approach for this is the following:

@Override
public void onResume() {
    super.onResume();
    initGpsTracker();
}

public synchronized void initGpsTracker() {
    if (mMap != null) {
        try {
            checkIfPermissionAllowedForLocation();
        } catch (SecurityException secex) {
            Toast.makeText(getActivity(), "not enabled in manifest", Toast.LENGTH_SHORT).show();
        }
    }
}

/**
 * rule is the following. First we check if the permissions are there. If not, we check if we can enable or not.
 * If the permissions are check if gps is enabled.
 */
private void checkIfPermissionAllowedForLocation() {

    //if permissions are set then we go to else, check for gps
    if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        // Request missing location permission.
        if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) {
            askIfToRequestPermissions();
        } else {
            requestPermissions();
        }
    } else {
        // Location permission has been granted, continue as usual.
        if (!isGpsProviderEnabled()) {
            askToEnableGPS();
        } else {
            mMap.setMyLocationEnabled(true);
        }
    }
}


private void askToEnableGPS() {
    CustomFragmentDialog customFragmentDialog = CustomFragmentDialog.newInstance(getString(R.string.enable_gps_title),
            getString(R.string.enable_gps_message),
            getString(R.string.ok),
            getString(R.string.cancel),
            callback);
    customFragmentDialog.show(getFragmentManager(), CUSTOM_TAG);
}

private void requestPermissions() {
    ActivityCompat.requestPermissions(getActivity(),
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            REQUEST_CODE_LOCATION);
}

private void enableGPS() {
    if (enableLocationService != null) {
        enableLocationService.askToEnableGps(locationCallback);
    }
}

private GoogleAskToEnableLocationService.GpsCallback locationCallback = new GoogleAskToEnableLocationService.GpsCallback() {
    @Override
    public void onSuccess() {
        initGpsTracker();
    }

    @Override
    public void onResolutionRequired(Status status) {
        try {
            status.startResolutionForResult(getActivity(), CONNECTION_RESOLUTION_CODE);
        } catch (IntentSender.SendIntentException setex) {
            Toast.makeText(getActivity(), "Exception in sending intent:", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onError() {
        Toast.makeText(getActivity(), "Location services unavailable!", Toast.LENGTH_SHORT).show();
    }
};

private void askIfToRequestPermissions() {
    CustomFragmentDialog customFragmentDialog = CustomFragmentDialog.newInstance(getString(R.string.enable_gps_title),
            getString(R.string.enable_permissions_message),
            getString(R.string.ok),
            getString(R.string.cancel),
            callback_permissions);
    customFragmentDialog.show(getFragmentManager(), CUSTOM_TAG);
}

private CustomFragmentDialog.Callback callback = new CustomFragmentDialog.Callback() {
    @Override
    public void onPositiveButtonClicked(Bundle bundle) {
        enableGPS();
    }

    @Override
    public void onNegativeButtonClicked(Bundle bundle) {

    }
};


private CustomFragmentDialog.Callback callback_permissions = new CustomFragmentDialog.Callback() {
    @Override
    public void onPositiveButtonClicked(Bundle bundle) {
        requestPermissions();
    }

    @Override
    public void onNegativeButtonClicked(Bundle bundle) {

    }
};

 private boolean isGpsProviderEnabled() {
    return googleUtils.isGpsProviderEnabled();
 }
 //inside it is a normal check if locationManager  can access
 //the gps provider
  if (locationManager.getProvider(provider) == null) {
      return false;
  }

Now, for request permission there are some callbacks:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case CONNECTION_RESOLUTION_CODE:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    initGpsTracker();
                    break;
                case Activity.RESULT_CANCELED:
                    Toast.makeText(getContext(), "Gps not enabled:", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
            break;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_CODE_LOCATION) {
        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            initGpsTracker();
        } else {
            Toast.makeText(getActivity(), "Manifest permission, not enabled", Toast.LENGTH_SHORT).show();

        }
    }
}

Now, finally the class where you deal with the request for the location so you do not have to go to settings:

 public void askToEnableGps(final GpsCallback callback) {
    if (locationClient == null) {
        return;
    }
    mLocationRequestHighAccuracy = LocationRequest.create();
    mLocationRequestHighAccuracy.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationRequestHighAccuracy.setInterval(30 * 1000);
    mLocationRequestHighAccuracy.setFastestInterval(5 * 1000);
    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
            .addLocationRequest(mLocationRequestHighAccuracy);
    builder.setAlwaysShow(true);
    PendingResult<LocationSettingsResult> result =
            LocationServices.SettingsApi.checkLocationSettings(locationClient, builder.build());

    result.setResultCallback(getResultCallback(callback));
}

private ResultCallback<LocationSettingsResult> getResultCallback(final GpsCallback callback) {

    return new ResultCallback<LocationSettingsResult>() {
        @Override
        public void onResult(LocationSettingsResult result) {
            if (result != null) {
                final Status status = result.getStatus();
                if (callback != null) {
                    //final LocationSettingsStates statesResult = result.getLocationSettingsStates();
                    switch (status.getStatusCode()) {
                        case LocationSettingsStatusCodes.SUCCESS:
                            // All location settings are satisfied. The client can initialize location
                            // requests here.
                            callback.onSuccess();
                            break;
                        case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                            // Location settings are not satisfied. But could be fixed by showing the user
                            // a dialog.
                            callback.onResolutionRequired(status);
                            break;
                        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                            // Location settings are not satisfied. However, we have no way to fix the
                            // settings so we won't show the dialog.
                            callback.onError();
                            break;
                    }
                }
            }
        }
    };
}

public interface GpsCallback {
    /**
     * callback method for when the result it is a success.
     */
    void onSuccess();

    /**
     * callback for when user interaction it is required.
     *
     * @param status the result from the service needed for the resolution.
     */
    void onResolutionRequired(Status status);

    /**
     * Callback for when the change it is not possible.
     */
    void onError();
}
Andrei T
  • 2,985
  • 3
  • 21
  • 28
  • Let me know if it worked for you. I am still refining this code so it might have some bugs inside, sorry for that. – Andrei T Jul 28 '16 at 19:00
  • All the above code can be found here: https://github.com/toaderandrei/live_tracking – Andrei T Jul 28 '16 at 19:05
  • 1
    your help is greatly appreciated. I ended up revising some of my code after looking at your example. Main problem, if you're using the Emulator in Android Studio - make sure you click SEND on the GPS settings page, otherwise the device never gets GPS coordinates. – Phil Jul 29 '16 at 14:34