1

So, I have custom Location Service class, from which I want to get last known location. It's possible, that I can call getLastKnownLocation() before GoogleApiClient is connected, so I have to wait for it and then call getLastKnownLocation(), but I have no clue how to manage that. I'm thinking that RxJava 2 can help me with that, but I'm not familiar with that framework yet. This is my class for now:

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.model.LatLng;

import javax.inject.Inject;

import pl.pancor.android.air.base.FragmentScope;

@FragmentScope
public class LocationService implements Location.Service,
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
        ActivityCompat.OnRequestPermissionsResultCallback {

    private static final String TAG = LocationService.class.getSimpleName();
    private static final int PERMISSIONS_REQUEST = 13;

    private GoogleApiClient mGoogleApiClient;

    private Activity mActivity;

    private android.location.Location mLastLocation;

    private Location.Receiver mReceiver;

    @Inject
    LocationService(Activity activity) {

        mActivity = activity;
    }

    @Override
    public void getLastKnownLocation() {

        if (isPermissionsGranted(true))
            getLocation();

    }

    /**
     * @param request if permissions aren't granted and {@param request} is true,
     *                then request permissions
     * @return true if location permissions are granted
     */
    private boolean isPermissionsGranted(boolean request) {

        if (ActivityCompat.checkSelfPermission(mActivity,
                Manifest.permission.ACCESS_FINE_LOCATION) !=
                PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(mActivity,
                        Manifest.permission.ACCESS_COARSE_LOCATION) !=
                        PackageManager.PERMISSION_GRANTED) {

            if (request) {
                ActivityCompat.requestPermissions(mActivity,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
                                Manifest.permission.ACCESS_COARSE_LOCATION},
                        PERMISSIONS_REQUEST);
            }
            return false;
        }
        return true;
    }

    private void getLocation() {

        if (mGoogleApiClient != null)

            mLastLocation = LocationServices.FusedLocationApi
                .getLastLocation(mGoogleApiClient);

        if (mLastLocation != null) {

            LatLng latLng = new LatLng(mLastLocation.getLatitude(),
                    mLastLocation.getLongitude());
            mReceiver.lastKnownLocation(latLng);
        } else {

            Log.e(TAG, "NULLLLLLLLLLLLLLLLLLLLLLL");
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {

    }

    @Override
    public void onConnectionSuspended(int i) {


    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }

    @Override
    public void setupReceiver(Location.Receiver receiver) {

        mReceiver = receiver;
    }

    @Override
    public void onStart() {

        if (mGoogleApiClient != null){

            mGoogleApiClient.connect();
        } else {

            mGoogleApiClient = getGoogleApiClient();
            mGoogleApiClient.connect();
        }
    }

    @Override
    public void onStop() {

        if (mGoogleApiClient != null)
            mGoogleApiClient.disconnect();
    }

    private GoogleApiClient getGoogleApiClient(){

        return new GoogleApiClient.Builder(mActivity)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {

        switch (requestCode){

            case PERMISSIONS_REQUEST:

                if (grantResults.length > 0 &&
                        grantResults[0] == PackageManager.PERMISSION_GRANTED){

                    getLastKnownLocation();
                } else {

                }
        }
    }
}

I need somehow to check if mGoogleApiClient is connected (mGoogleApiClient.isConnected()) and if not, wait to connect, and then get location from FusedLocationApi, but I don't want to put methods to onConnected(), because it will sometimes return location, when I don't want to return location.

ppreetikaa
  • 1,149
  • 2
  • 15
  • 22
Panczur
  • 633
  • 1
  • 9
  • 26
  • Can you explain in a little more detail what do you mean by: "but i don't want to put methods to onConnected(), becosue it will sometimes return location, when i don't want to return location." – mjekov Mar 08 '17 at 15:57
  • My presenter calls mLocationService.getLastKnownLocation(), then in LocationService i could build mGoogleApiClient and connect it, and in onConnected() put mReceiver.lastKnownLocation(latLng) and that will send position back to the presenter. I build and connect mGoogleApiClient faster (onStart() is called in onViewCreated() in fragment) and sometimes when i call getLastKnownLocation(), mGoogleApiClient is not connected yet, so i have to wait for it. And now i don't know how to wait for it without putting mReceiver.lastKnownLocation(latLng) in onConnected(). – Panczur Mar 08 '17 at 16:07

2 Answers2

1

After your explanations in the comment section, I would do something like this:

  1. in the Receiver/Fragment class I would put some logic that sets a variable "updateUI" to true in the LocationService class, when it is appropriate for onConnected to call the mReceiver.lastKnownLocation(latLng) method. The default value will be false, and if onConnected gets called before the receiver is ready, the method mReceiver.lastKnownLocation(latLng) won't be called.

  2. Another approach is to store always the last known location in your SharedPreferences (or at least in the onPause method). Then, you can always use it the first time you need a location and wait for more precise location later, but this method won't be so precise at start time.

mjekov
  • 682
  • 2
  • 17
  • 32
1

So, after some time, i manage to make it and also finished my entire class and i would like to share with you, what i did

public interface Location {

    interface Service extends BaseLocation<Receiver>{

        void onStart();

        void onStop();

        void onActivityResult(int requestCode, int resultCode);

        void getLastKnownLocation();
    }

    interface Receiver{

        void lastKnownLocation(double latitude, double longitude);

        void userRefusedToSendLocation();

        void unableToObtainLocation();
    }
}



import android.Manifest;
import android.app.Activity;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStatusCodes;

import javax.inject.Inject;

import pl.pancor.android.air.base.FragmentScope;

@FragmentScope
public class LocationService implements Location.Service,
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
        ActivityCompat.OnRequestPermissionsResultCallback, LocationListener,
        ResultCallback<LocationSettingsResult>{

    private static final int PERMISSIONS_REQUEST = 13;
    private static final int SETTINGS_CHECK = 23;
    private static final int GOOGLE_API_CLIENT_ERROR = 33;

    private static final int LOCATION_EXPIRATION_TIME = 10 * 1000;
    private static final int LOCATION_INTERVAL = 2 * 1000;

    private GoogleApiClient mGoogleApiClient;

    private Activity mActivity;

    private LocationRequest mLocationRequest;
    private android.location.Location mLastLocation;

    private Location.Receiver mReceiver;

    private Handler mHandler;
    private final Runnable mExpiredLocationUpdate = new Runnable() {
        @Override
        public void run() {
            mReceiver.unableToObtainLocation();
        }
    };

    private boolean isWaitingForConnect = false;

    @Inject
    LocationService(Activity activity) {

        mActivity = activity;
    }

    @Override
    public void getLastKnownLocation() {

        if (isPermissionsGranted(true))
            checkLocationSettings();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode) {

        resolveProblems(requestCode, resultCode);
    }

    @Override
    public void onLocationChanged(android.location.Location location) {

        if (mLastLocation == null) {

            mLastLocation = location;
            sendLatLngToReceiver();
        }

        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        mHandler.removeCallbacks(mExpiredLocationUpdate);
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {

        if (isWaitingForConnect)
            getLastKnownLocation();
    }

    @Override
    public void onConnectionSuspended(int i) {

        //mGoogleApiClient will automatically try to reconnect
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult result) {

        if (!result.hasResolution()){
            mReceiver.unableToObtainLocation();
            GoogleApiAvailability.getInstance()
                    .getErrorDialog(mActivity, result.getErrorCode(), 0).show();
            return;
        }
        if (mActivity.hasWindowFocus()) {
            try {
                result.startResolutionForResult(mActivity, GOOGLE_API_CLIENT_ERROR);
            } catch (IntentSender.SendIntentException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void setupReceiver(Location.Receiver receiver) {

        mReceiver = receiver;
    }

    @Override
    public void onStart() {

        mHandler = new Handler();

        if (mGoogleApiClient != null){

            mGoogleApiClient.connect();
        } else {

            mGoogleApiClient = getGoogleApiClient();
            mGoogleApiClient.connect();
        }
    }

    @Override
    public void onStop() {

        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(
                    mGoogleApiClient, this);

            mGoogleApiClient.disconnect();
        }
        mHandler.removeCallbacks(mExpiredLocationUpdate);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {

        switch (requestCode){
            case PERMISSIONS_REQUEST:

                if (grantResults.length > 0 &&
                        grantResults[0] == PackageManager.PERMISSION_GRANTED){

                    getLastKnownLocation();
                } else {

                    mReceiver.userRefusedToSendLocation();
                }
        }
    }

    @Override
    public void onResult(@NonNull LocationSettingsResult result) {

        final Status status = result.getStatus();
        switch (status.getStatusCode()){
            case LocationSettingsStatusCodes.SUCCESS:

                getLocation();
                break;
            case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:

                if (mActivity.hasWindowFocus()) {
                    try {
                        status.startResolutionForResult(mActivity, SETTINGS_CHECK);
                    } catch (IntentSender.SendIntentException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case  LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:

                mReceiver.unableToObtainLocation();
                break;
        }
    }

    private void checkLocationSettings() {

        if (mGoogleApiClient != null){

            mLocationRequest = new LocationRequest()
                    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                    .setFastestInterval(LOCATION_INTERVAL / 2)
                    .setInterval(LOCATION_INTERVAL)
                    .setNumUpdates(1);

            LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                    .addLocationRequest(mLocationRequest);

            PendingResult<LocationSettingsResult> result = LocationServices
                    .SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
            result.setResultCallback(this);
        }
    }

    private void getLocation(){

        if (mGoogleApiClient != null)
            mLastLocation = LocationServices.FusedLocationApi
                    .getLastLocation(mGoogleApiClient);

        sendLatLngToReceiver();
    }

    private void sendLatLngToReceiver(){

        if (mLastLocation != null) {

            mReceiver.lastKnownLocation(mLastLocation.getLatitude(),
                    mLastLocation.getLongitude());
            mHandler.removeCallbacks(mExpiredLocationUpdate);
        } else {

            requestLocation();
        }
    }

    private void requestLocation(){

        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {

            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);
            mHandler.postDelayed(mExpiredLocationUpdate, LOCATION_EXPIRATION_TIME);
        } else {

            isWaitingForConnect = true;
        }
    }

    /**
     * @param request if permissions aren't granted and {@param request} is true,
     *                then request permissions
     * @return true if location permissions are granted
     */
    private boolean isPermissionsGranted(boolean request) {

        if (ActivityCompat.checkSelfPermission(mActivity,
                Manifest.permission.ACCESS_FINE_LOCATION) !=
                PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(mActivity,
                        Manifest.permission.ACCESS_COARSE_LOCATION) !=
                        PackageManager.PERMISSION_GRANTED) {

            if (request) {
                ActivityCompat.requestPermissions(mActivity,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
                                Manifest.permission.ACCESS_COARSE_LOCATION},
                        PERMISSIONS_REQUEST);
            }
            return false;
        }
        return true;
    }

    private GoogleApiClient getGoogleApiClient(){

        return new GoogleApiClient.Builder(mActivity)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    private void resolveProblems(int requestCode, int resultCode){

        switch (requestCode){
            case SETTINGS_CHECK:
                switch (resultCode){
                    case Activity.RESULT_OK:
                        getLastKnownLocation();
                        break;
                    case Activity.RESULT_CANCELED:
                        mReceiver.userRefusedToSendLocation();
                        break;
                }
                break;
            case GOOGLE_API_CLIENT_ERROR:
                switch (resultCode) {
                    case Activity.RESULT_OK:
                        mGoogleApiClient.connect();
                        break;
                    case Activity.RESULT_CANCELED:
                        mReceiver.unableToObtainLocation();
                        break;
                }
        }
    }
}
Panczur
  • 633
  • 1
  • 9
  • 26