Last active
June 21, 2018 09:25
-
-
Save aftabsikander/cc4c843e7cef21439cc45bc614dd3d52 to your computer and use it in GitHub Desktop.
Location Services Utilities
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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