Skip to content

Instantly share code, notes, and snippets.

@aftabsikander
Last active June 21, 2018 09:25
Show Gist options
  • Save aftabsikander/cc4c843e7cef21439cc45bc614dd3d52 to your computer and use it in GitHub Desktop.
Save aftabsikander/cc4c843e7cef21439cc45bc614dd3d52 to your computer and use it in GitHub Desktop.
Location Services Utilities
package com.app.example.location;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import com.app.example.interfaces.FusedLocationResultCallback;
import com.app.example.model.location.LocationModel;
import org.greenrobot.eventbus.EventBus;
import timber.log.Timber;
public class FetchUserLocationService extends IntentService implements
FusedLocationResultCallback {
private LocationFetch mLocationProvider;
private LocationModel locationModel;
public FetchUserLocationService() {
super("FetchUserLocationService");
}
/**
* Starts this service to perform Location fetch request Baz. If
* the service is already performing a task this action will be queued.
*
* @see IntentService
*/
// TODO: Customize helper method
public static void startFetchRequest(Context context) {
Intent intent = new Intent(context, FetchUserLocationService.class);
context.startService(intent);
}
@Override
public void onCreate() {
super.onCreate();
setupLocationListener();
Timber.d("onCreate called");
}
@Override
public void onDestroy() {
super.onDestroy();
/*if (mLocationProvider != null)
mLocationProvider.disconnect();*/
Timber.d("onDestroy called");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
}
}
//region Google Client Helper methods
/***
* This is used to setup Location fetching process
*/
private void setupLocationListener() {
//TODO Request location fetch listener
mLocationProvider = new LocationFetch(this, this, true);
}
private void disconnectLocationListener() {
if (mLocationProvider != null)
mLocationProvider.disconnect();
}
//endregion
//region Helper methods for Location Manager
@Override
public void handleNewLocation(LocationModel location) {
Timber.d("Location called from Intent Service");
if (location != null) {
locationModel = location;
new GetAddressTask(this).execute(locationModel);
//String fetchAddressValue = locationModel.getAddressString(this);
//sendUserLocationToApp(locationModel, fetchAddressValue);
disconnectLocationListener();
}
}
private void sendUserLocationToApp(LocationModel locationModel, String addressFetch) {
EventBus.getDefault().post(new LocationEvents(locationModel, addressFetch));
}
private class GetAddressTask extends AsyncTask<LocationModel, Void, String> {
private Context mContext;
private LocationModel locationModel;
public GetAddressTask(Context context) {
super();
mContext = context;
}
@Override
protected String doInBackground(LocationModel... params) {
locationModel = params[0];
return locationModel.getAddressString(mContext);
}
@Override
protected void onPostExecute(String fetchAddressValue) {
super.onPostExecute(fetchAddressValue);
sendUserLocationToApp(locationModel, fetchAddressValue);
}
}
//endregion
}
package com.app.example.interfaces;
import com.app.example.model.location.LocationModel;
/**
* Created by afali on 9/27/17.
*
*/
public interface FusedLocationResultCallback {
/***
* Provide callback for location returned by Google Fused location client
* @param location Return user current location
*/
void handleNewLocation(LocationModel location);
}
package com.app.example.interfaces;
import com.google.android.gms.common.api.ResolvableApiException;
/**
* Created by afali on 9/27/17.
*
*/
public interface FusedLocationSettingCallback {
/****
* Open Setting intent
* @param callbackResult
*/
void performSettingIntent(int callbackResult);
/****
* Handle setting callback using setting client google client
* @param res
*/
void handleSettingIntent(ResolvableApiException res);
}
package com.app.example.location;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import com.app.example.interfaces.FusedLocationResultCallback;
import com.app.example.interfaces.FusedLocationSettingCallback;
import com.app.example.model.location.LocationModel;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.location.SettingsClient;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executors;
import timber.log.Timber;
/**
* Created by afali on 7/15/2015.
*/
/**
* This class is used to get the lat lng of a current location
*/
public class LocationFetch {
public static final String TAG = "LocationProvider";
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
/**
* The fastest rate for active location updates. Exact. Updates will never be more frequent
* than this value.
*/
public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
UPDATE_INTERVAL_IN_MILLISECONDS / 2;
public static final long LOCATION_MANAGER_TIMEOUT_FOR_ACTIVITY = 12000; //12 Sec
public static final long LOCATION_MANAGER_TIMEOUT_FOR_SERVICE = 15000; // 15 Sec
public static final long LOCATION_MANAGER_TIMEOUT_FOR_ACTIVITY_SERVICE = 5000; // 5 Sec
private static final long ONE_MIN = 1000 * 60;
private static final long TWO_MIN = ONE_MIN * 2;
private static final long FIVE_MIN = ONE_MIN * 5;
private static final float MIN_ACCURACY = 25.0f;
private static final float MIN_LAST_READ_ACCURACY = 500.0f;
protected Location mBestReadingLocation;
protected LocationModel locationModelForAAP;
Executors executors;
Handler handlerForLocation = null;
/**
* Constant used in the location settings dialog.
*/
public static final int REQUEST_CHECK_SETTINGS_DIALOG = 0x1;
/**
* Constant used in the location settings Screen.
*/
public static final int REQUEST_CHECK_SETTINGS_SCREEN = 0x2;
/***
* FusedLocation Setting callback
*/
private FusedLocationSettingCallback fusedLocationSettingCallback;
/**
* Provides access to the Fused Location Provider API.
*/
private FusedLocationProviderClient fusedLocationProviderClient;
/**
* Provides access to the Location Settings API.
*/
private SettingsClient mSettingsClient;
/**
* Stores parameters for requests to the FusedLocationProviderApi.
*/
private LocationRequest mLocationRequest;
/**
* Stores the types of location services the client is interested in using. Used for checking
* settings to determine if the device has optimal location settings.
*/
private LocationSettingsRequest mLocationSettingsRequest;
/**
* Callback for Location events.
*/
private LocationCallback mLocationCallback;
private FusedLocationResultCallback fusedLocationCallback;
private GetLocation getLocationRunnable;
private WeakReference<Activity> mActivity;
private boolean isForService = false;
private boolean isForActivityService = false;
private Context mContext;
public LocationFetch(Context context, FusedLocationResultCallback callback,
boolean fromService) {
mContext = context;
setupFusedLocationClient(context);
setupSettingClient(context);
fusedLocationCallback = callback;
isForService = fromService;
setupLocationRequest();
createLocationResultCallback(true);
buildLocationSettingsRequest();
}
public LocationFetch(Context context, FusedLocationResultCallback callback, boolean
fromService, boolean forActivityService) {
mContext = context;
setupFusedLocationClient(context);
setupSettingClient(context);
fusedLocationCallback = callback;
isForService = fromService;
isForActivityService = forActivityService;
setupLocationRequest();
createLocationResultCallback(true);
buildLocationSettingsRequest();
}
public LocationFetch(Activity activity, Context context,
FusedLocationResultCallback callback,
FusedLocationSettingCallback settingCallback) {
mContext = context;
setupFusedLocationClient(context);
setupSettingClient(context);
mActivity = new WeakReference<Activity>(activity);
fusedLocationCallback = callback;
fusedLocationSettingCallback = settingCallback;
// Create the LocationRequest object
setupLocationRequest();
createLocationResultCallback(false);
buildLocationSettingsRequest();
startSettingClientLocationUpdate();
}
//region Helper methods for Client setup
private void setupFusedLocationClient(Context context) {
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
}
private void setupSettingClient(Context context) {
mSettingsClient = LocationServices.getSettingsClient(context);
}
//endregion
//region Helper methods for request setup
private void setupLocationRequest() {
mLocationRequest = new LocationRequest();
// Sets the desired interval for active location updates. This interval is
// inexact. You may not receive updates at all if no location sources are available, or
// you may receive them slower than requested. You may also receive updates faster than
// requested if other applications are requesting location at a faster interval.
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
// Sets the fastest rate for active location updates. This interval is exact, and your
// application will never receive updates faster than this value.
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* Uses a {@link com.google.android.gms.location.LocationSettingsRequest.Builder} to build
* a {@link com.google.android.gms.location.LocationSettingsRequest} that is used for checking
* if a device has the needed location settings.
*/
private void buildLocationSettingsRequest() {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
mLocationSettingsRequest = builder.build();
}
//endregion
//region Helper methods for Handler and Runnable
private void setupLocationHandler() {
Log.i(TAG, "Handler Created.");
handlerForLocation = new Handler();
getLocationRunnable = new GetLocation();
if (isForService) {
getLocationRunnable.sendResultToService = true;
} else {
getLocationRunnable.mRunnableActivity = new WeakReference<Activity>(mActivity.get());
}
if (isForService) {
if (isForActivityService) {
handlerForLocation.postDelayed(getLocationRunnable,
LOCATION_MANAGER_TIMEOUT_FOR_ACTIVITY_SERVICE);
} else {
handlerForLocation.postDelayed(getLocationRunnable,
LOCATION_MANAGER_TIMEOUT_FOR_SERVICE);
}
} else
handlerForLocation.postDelayed(getLocationRunnable,
LOCATION_MANAGER_TIMEOUT_FOR_ACTIVITY);
}
public void cancelHandlerAndLocationUpdate() {
Log.i(TAG, "Manager Cancelled");
// It is a good practice to remove location requests when the activity is in a paused or
// stopped state. Doing so helps battery performance and is especially
// recommended in applications that request frequent location updates.
fusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
if (handlerForLocation != null) handlerForLocation.removeCallbacks(getLocationRunnable);
}
//endregion
//region Helper methods for life cycle events
public void disconnect() {
cancelHandlerAndLocationUpdate();
}
//endregion
//region Helper methods for Location creation Model
private void getLatestLocationModel(Location latestLocation) {
locationModelForAAP = new LocationModel();
if (latestLocation != null) {
locationModelForAAP.setLat(latestLocation.getLatitude());
locationModelForAAP.setLng(latestLocation.getLongitude());
}
}
private Location createZeroCoordinateLocation() {
Location location = new Location("");
location.setLatitude(0.0d);
location.setLongitude(0.0d);
return location;
}
//endregion
//region Internal helper methods
private boolean checkLocationPermission() {
return !(ActivityCompat.checkSelfPermission(mContext,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(mContext,
Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED);
}
private Location bestLastKnownLocation(Location currentLocation,
float minAccuracy, long minTime) {
Location bestResult = null;
float bestAccuracy = Float.MAX_VALUE;
long bestTime = Long.MIN_VALUE;
if (checkLocationPermission()) {
// Get the best most recent location currently available
if (currentLocation != null) {
float accuracy = currentLocation.getAccuracy();
long time = currentLocation.getTime();
if (accuracy < bestAccuracy) {
bestResult = currentLocation;
bestAccuracy = accuracy;
bestTime = time;
}
}
// Return best reading or null
if (bestAccuracy > minAccuracy || bestTime < minTime) {
return null;
} else {
return bestResult;
}
} else {
return null;
}
}
//endregion
//region Helper methods for location result callbacks
/**
* Creates a callback for receiving location events.
*/
private void createLocationResultCallback(boolean comingForService) {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
Log.i(TAG, "Location result callback.");
mBestReadingLocation = locationResult.getLastLocation();
if (checkLocationPermission()) {
// Get best last location measurement meeting criteria
mBestReadingLocation = bestLastKnownLocation(mBestReadingLocation,
MIN_LAST_READ_ACCURACY, FIVE_MIN);
if (null == mBestReadingLocation || mBestReadingLocation.getAccuracy()
> MIN_LAST_READ_ACCURACY || mBestReadingLocation.getTime()
< System.currentTimeMillis() - TWO_MIN) {
// Schedule a runnable to unregister location listeners
} else {
cancelHandlerAndLocationUpdate();
getLatestLocationModel(mBestReadingLocation);
fusedLocationCallback.handleNewLocation(locationModelForAAP);
}
} else {
cancelHandlerAndLocationUpdate();
getLatestLocationModel(createZeroCoordinateLocation());
fusedLocationCallback.handleNewLocation(locationModelForAAP);
}
}
};
if (comingForService) {
startLocationUpdates();
}
}
/****
* This starts location updates and initialize location handler runnable
*/
public void startLocationUpdates() {
if (checkLocationPermission()) {
fusedLocationProviderClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper());
setupLocationHandler();
}
}
private void startSettingClientLocationUpdate() {
// Begin by checking if the device has the necessary location settings.
mSettingsClient.checkLocationSettings(mLocationSettingsRequest)
.addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
Timber.i("All location settings are satisfied.");
startLocationUpdates();
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
Timber.i("Location settings are not satisfied. " +
"Attempting to upgrade location settings ");
// try {
// Show the dialog by calling startResolutionForResult(), and check the
// result in onActivityResult().
fusedLocationSettingCallback.handleSettingIntent(
(ResolvableApiException) e);
/*ResolvableApiException rae = ;
rae.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS_DIALOG);*/
/* } catch (IntentSender.SendIntentException sie) {
Log.i(TAG, "PendingIntent unable to execute request.");
}*/
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
String errorMessage = "Location settings are inadequate, and cannot be " +
"fixed here. Fix in Settings.";
Log.e(TAG, errorMessage);
fusedLocationSettingCallback.performSettingIntent(
REQUEST_CHECK_SETTINGS_SCREEN);
}
}
});
}
//endregion
private class GetLocation implements Runnable {
private WeakReference<Activity> mRunnableActivity;
private boolean sendResultToService = false;
@Override
public void run() {
Log.i(TAG, "Runnable Called");
if (sendResultToService) {
Log.i(TAG, "Runnable Called For Service");
if (mBestReadingLocation == null) {
cancelHandlerAndLocationUpdate();
getLatestLocationModel(createZeroCoordinateLocation());
fusedLocationCallback.handleNewLocation(locationModelForAAP);
} else {
cancelHandlerAndLocationUpdate();
getLatestLocationModel(createZeroCoordinateLocation());
fusedLocationCallback.handleNewLocation(locationModelForAAP);
}
} else {
Log.i(TAG, "Runnable Called For Activity");
Activity activity = mRunnableActivity.get();
if (activity != null) {
if (mBestReadingLocation == null) {
cancelHandlerAndLocationUpdate();
getLatestLocationModel(createZeroCoordinateLocation());
fusedLocationCallback.handleNewLocation(locationModelForAAP);
} else {
cancelHandlerAndLocationUpdate();
getLatestLocationModel(createZeroCoordinateLocation());
fusedLocationCallback.handleNewLocation(locationModelForAAP);
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment