Location Updates using google play services and FusedLocationProviderClient

5.3k views Asked by At

I want to get location updates on a "background service" using latest fused location provider client.I don't want to use the location listeners and Google API Client that all are using. I also need to use location settings Api provided by google play services to check whether location setting are disable or enable on that "background service".Please help.

5

There are 5 answers

0
inkedTechie On

You can write the code for the intentService as given below. It has been considered that since it is running in the background, we would not show the permission dialogs.

public class GetLocationService extends IntentService
    implements
    GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener, LocationListener {
public static String TAG = GetLocationService.class.getSimpleName();
private GoogleApiClient mGoogleApiClient;


public GetLocationService() {
    super(TAG);
}

/**
 * Creates an IntentService.  Invoked by your subclass's constructor.
 *
 * @param name Used to name the worker thread, important only for debugging.
 */
public GetLocationService(String name) {
    super(name);
}

@Override
protected void onHandleIntent(Intent intent) {
    if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
        Log.d(TAG, "disconnecting");
        mGoogleApiClient.disconnect();
    }
    Log.d(TAG, "onHandleIntent");
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();
    mGoogleApiClient.connect();
}

@Override
public void onConnected(@Nullable Bundle bundle) {
    LocationRequest locationRequest = LocationRequest.create();
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    LocationRequest locationRequestLow = LocationRequest.create();
    locationRequestLow.setPriority(LocationRequest.PRIORITY_LOW_POWER);
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==
            PackageManager.PERMISSION_GRANTED) {
        // TODO: Consider calling
        //    ActivityCompat#requestPermissions
        // here to request the missing permissions, and then overriding
        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
        //                                          int[] grantResults)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, this);
    } else {
        Log.d(TAG, "permission denied");
    }
}

@Override
public void onConnectionSuspended(int i) {

}

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

}

@Override
public void onLocationChanged(Location location) {
    Log.d(TAG, location.toString());    
}

}

2
Ameer On

You can extend the service class like

public class BackgroundLocationService extends Service implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener

You can check the full source code here BackgroundLocationService.java

For enable location settings

   private void displayLocationSettingsRequest() {
    GoogleApiClient googleApiClient = new GoogleApiClient.Builder(context)
            .addApi(LocationServices.API).build();
    googleApiClient.connect();

    LocationRequest locationRequest = LocationRequest.create();
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationRequest.setInterval(10000);
    locationRequest.setFastestInterval(10000 / 2);

    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
    builder.setAlwaysShow(true);

    PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build());
    result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
        @Override
        public void onResult(@NonNull LocationSettingsResult result) {
            final Status status = result.getStatus();
            switch (status.getStatusCode()) {
                case LocationSettingsStatusCodes.SUCCESS:
                    break;
                case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:

                    try {
                        // Show the dialog by calling startResolutionForResult(), and check the result
                        // in onActivityResult().
                        status.startResolutionForResult(context, REQUEST_CHECK_SETTINGS);
                    } catch (IntentSender.SendIntentException ignored) {
                    }
                    break;
                case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                    break;
            }
        }
    });
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
   // Check for the integer request code originally supplied to 
    startResolutionForResult().
        case REQUEST_CHECK_SETTINGS:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    //startLocationUpdates();
                    break;
                case Activity.RESULT_CANCELED:
                    displayLocationSettingsRequest();//keep asking
                    showToast("Location permission needed");
                    break;
            }
            break;
    }
}

You can check location settings from Service and if location is off ,

LocationManager lm = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
  boolean gps_enabled = false;
      boolean network_enabled = false;

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

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

   if(!gps_enabled && !network_enabled) {
// notify user
 // Either you can display a Notification(Recommended way) or you can show dialog with 

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
//It needs SYSTEM_ALERT_WINDOW permission. Add this permission in Manifest.

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

 }
0
Saveen On

Hi I have good understanding about location services and how we can use in it perfect way. See i'll give you idea about fused apis.

Just make sure follow the below steps. It is pretty nice and simple. Step 1. Make this class GoogleLocationService.java

public class GoogleLocationService {
private GoogleServicesCallbacks callbacks = new GoogleServicesCallbacks();
LocationUpdateListener locationUpdateListener;
Context activity;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;

public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 30000;


public GoogleLocationService(Context activity, LocationUpdateListener locationUpdateListener) {
    this.locationUpdateListener = locationUpdateListener;
    this.activity = activity;
    buildGoogleApiClient();
}

protected synchronized void buildGoogleApiClient() {
    //Log.i(TAG, "Building GoogleApiClient");
    mGoogleApiClient = new GoogleApiClient.Builder(activity)
            .addConnectionCallbacks(callbacks)
            .addOnConnectionFailedListener(callbacks)
            .addApi(LocationServices.API)
            .build();
    createLocationRequest();
    mGoogleApiClient.connect();
}

protected void createLocationRequest() {
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

}

private class GoogleServicesCallbacks implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    @Override
    public void onConnected(Bundle bundle) {
        startLocationUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        mGoogleApiClient.connect();
    }

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

        if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) {
            Toast.makeText(activity, "Google play service not updated", Toast.LENGTH_LONG).show();

        }
        locationUpdateListener.cannotReceiveLocationUpdates();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location.hasAccuracy()) {
            if (location.getAccuracy() < 30) {
                locationUpdateListener.updateLocation(location);
            }
        }
    }
}

private static boolean locationEnabled(Context context) {
    boolean gps_enabled = false;
    LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    try {
        gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return gps_enabled;
}

private boolean servicesConnected(Context context) {
    return isPackageInstalled(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, context);
}

private boolean isPackageInstalled(String packagename, Context context) {
    PackageManager pm = context.getPackageManager();
    try {
        pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
        return true;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
        return false;
    }
}


public void startUpdates() {
    /*
     * Connect the client. Don't re-start any requests here; instead, wait
     * for onResume()
     */
    if (servicesConnected(activity)) {
        if (locationEnabled(activity)) {
            locationUpdateListener.canReceiveLocationUpdates();
            startLocationUpdates();
        } else {
            locationUpdateListener.cannotReceiveLocationUpdates();
            Toast.makeText(activity, "Unable to get your location.Please turn on your device Gps", Toast.LENGTH_LONG).show();
        }
    } else {
        locationUpdateListener.cannotReceiveLocationUpdates();
        Toast.makeText(activity, "Google play service not available", Toast.LENGTH_LONG).show();
    }
}

//stop location updates
public void stopUpdates() {
    stopLocationUpdates();
}

//start location updates
private void startLocationUpdates() {

    if (checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(activity, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, callbacks);
    }
}

public void stopLocationUpdates() {
    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, callbacks);
    }
}

public void startGoogleApi() {
    mGoogleApiClient.connect();
}

public void closeGoogleApi() {
    mGoogleApiClient.disconnect();
}

 }

Step2. Make this interface LocationUpdateListener.java

 public interface LocationUpdateListener {

/**
 * Called immediately the service starts if the service can obtain location
 */
void canReceiveLocationUpdates();

/**
 * Called immediately the service tries to start if it cannot obtain location - eg the user has disabled wireless and
 */
void cannotReceiveLocationUpdates();

/**
 * Called whenever the location has changed (at least non-trivially)
 * @param location
 */
void updateLocation(Location location);

/**
 * Called when GoogleLocationServices detects that the device has moved to a new location.
 * @param localityName The name of the locality (somewhere below street but above area).
 */
void updateLocationName(String localityName, Location location);
}

Step 3. Make this Location service class LocationService.java

public class LocationService extends Service {

private GoogleLocationService googleLocationService;

@Override
public void onCreate() {
    super.onCreate();
    //start the handler for getting locations
    //create component

    updateLocation(getApplicationContext());
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return Service.START_STICKY;
}

//get current location os user
private void updateLocation(Context context) {
    googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() {
        @Override
        public void canReceiveLocationUpdates() {
        }

        @Override
        public void cannotReceiveLocationUpdates() {
        }

        //update location to our servers for tracking purpose
        @Override
        public void updateLocation(Location location) {
            if (location != null ) {
                Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());

            }
        }

        @Override
        public void updateLocationName(String localityName, Location location) {

            googleLocationService.stopLocationUpdates();
        }
    });
    googleLocationService.startUpdates();
}


IBinder mBinder = new LocalBinder();


public class LocalBinder extends Binder {
    public LocationService getServerInstance() {
        return LocationService.this;
    }
}

@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

//stop location updates on stopping the service
@Override
public void onDestroy() {
    super.onDestroy();
    if (googleLocationService != null) {
        googleLocationService.stopLocationUpdates();
    }
}
}

Edited Answer:

How to use service,

Start service in your main activity,like this

startService(new Intent(context, LocationService.class));

for stop,

stopService(new Intent(context, LocationService.class));

and declare in manifest like this,

<service
 android:name=".service.location.LocationService"
 android:enabled="true"></service>

Note: I have done this because i need location after killed the app.If you dont want service.Then,you can call directly below code in class where location need to be update and remove locationservice.

 private GoogleLocationService googleLocationService;

 googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() {
    @Override
    public void canReceiveLocationUpdates() {
    }

    @Override
    public void cannotReceiveLocationUpdates() {
    }

    //update location to our servers for tracking purpose
    @Override
    public void updateLocation(Location location) {
        if (location != null ) {
            Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());

        }
    }

    @Override
    public void updateLocationName(String localityName, Location location) {

        googleLocationService.stopLocationUpdates();
    }
});
googleLocationService.startUpdates();


and call this onDestroy 
if (googleLocationService != null) {
    googleLocationService.stopLocationUpdates();
}

Remember Locationservice should be declare in manifest as well. According to me this is the best solution. Thanks hope this will help you

6
Dhaval Solanki On

if we need to use fusion location api then we need to use Google API client, For the background service it is not an issue to use it, Following is the example which I used for getting location update in background, But one more thing as Google introduces 'DOZE' mode from 6.0 so I can not give surety give update when your device in 'DOZE' mode,

import android.Manifest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
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.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

/**
 * Created By Dhaval Solanki
 */

public class ConnectionService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
    String TAG = "ConnectionService";


    private GoogleApiClient mGoogleApiClient;
    private LocationRequest mLocationRequest;
    private Location previousLocation;
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 30000;
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
            UPDATE_INTERVAL_IN_MILLISECONDS / 2;


    private Location mLastLocation;
    private Context context;

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind");
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        context = getApplicationContext();
        Log.i(TAG, "onStartCommand");
        if (checkPermsion(context)) {
            setupLocationService(context);
        }
        return Service.START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        ApplicationCLass.isServiceRunning = true;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "onUnbind");
        ApplicationCLass.isServiceRunning = false;
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        ApplicationCLass.isServiceRunning = false;
        super.onDestroy();
    }

    private void setupLocationService(Context context) {
        if (checkPlayServices()) {
            mGoogleApiClient = new GoogleApiClient.Builder(context)
                    .addApi(LocationServices.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();
            createLocationRequest();
        }
    }

    protected void createLocationRequest() {
        mLocationRequest = new LocationRequest().create();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        mGoogleApiClient.connect();
    }

    public boolean checkPermsion(Context context) {
        int MyVersion = Build.VERSION.SDK_INT;
        if (MyVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
            if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return false;
            } else if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }

    private boolean checkPlayServices() {
        GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
        int result = googleAPI.isGooglePlayServicesAvailable(this);
        if (result != ConnectionResult.SUCCESS) {
            return false;
        }
        return true;
    }


    private void startLocationUpdates() {
        if (mGoogleApiClient.isConnected()) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            if (Prefs.getUserLat(context).trim().length() > 0 && Prefs.getUserLong(context).trim().length() > 0) {
            } else {
                mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
                        mGoogleApiClient);
                if (mLastLocation != null) {
                    Prefs.setUserLat(context, String.valueOf(mLastLocation.getLatitude()));
                    Prefs.setUserLong(context, String.valueOf(mLastLocation.getLongitude()));
                }
            }
            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Log.i(TAG, "Connected to onConnected");
        startLocationUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.i(TAG, "Connected to onConnectionSuspended");
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Log.i(TAG, "Connected to onConnectionFailed");
    }

    @Override
    public void onLocationChanged(Location location) {
        try {
            Logger.print("onLocationChanged", "latitued :" + location.getLatitude() + " ,, Longitude " + location.getLongitude());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


}

One another alaram service continues runnning or not

import android.app.ActivityManager;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class AlarmService extends IntentService {
    @Override
    protected void onHandleIntent(Intent intent) {
        boolean isServiceRunning = false;
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (ConnectionService.class.getName().equals(service.service.getClassName())) {
                isServiceRunning = true;
                break;
            }
        }
        Log.i("AlarmService", "Service is running " + isServiceRunning);
        if(!isServiceRunning) {
            startService(new Intent(getApplicationContext(),ConnectionService.class));
        } else {
            ConnectionService.checkConnectionAndConnect(getApplicationContext());
        }
    }

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     */
    public AlarmService() {
        super("AlaramService");
    }
}

And Finaly start service

private void checkAndStartService() {
        final ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        new AsyncTask<Void, Void, Boolean>() {
            boolean isServiceRunning = false;

            @Override
            protected Boolean doInBackground(Void... params) {
                for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
                    if (ConnectionService.class.getName().equals(service.service.getClassName())) {
                        isServiceRunning = true;
                        break;
                    }
                }
                return isServiceRunning;
            }

            @Override
            protected void onPostExecute(Boolean aBoolean) {
                super.onPostExecute(aBoolean);
                Log.i("onPostExecute", "Service running = " + aBoolean);
                if (!aBoolean) {
                    startService(new Intent(ActivitySplash.this, ConnectionService.class));
                    Intent i = new Intent(ActivitySplash.this, AlarmService.class);
                    PendingIntent pi = PendingIntent.getService(ActivitySplash.this, 0, i, 0);
                    AlarmManager am = (AlarmManager) ActivitySplash.this.getSystemService(Context.ALARM_SERVICE);
                    am.cancel(pi); // cancel any existing alarms
                    am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 60000, pi);
                }
            }
        }.execute();
    }
0
Vlad On

The question does not appear to be very specific. It seems like you're having trouble starting up with using location services.

The best idea would probably be checking out the Google samples (https://github.com/googlesamples/android-play-location) to better understand the mechanics. I would probably start by implementing the simplest example (LocationUpdates). It uses the FusedLocationProvider and also checks if the settings are enabled, so it should fit your needs, or at least get you started.

If you really need to use a service you can check out LocationUpdatesForegroundService or LocationUpdatesPendingIntent. But I would strongly recommend to read and understand the first example before you do this.