Created
July 10, 2016 07:47
-
-
Save sydneyitguy/50cc6bba66a60ff0404875803a87e58f to your computer and use it in GitHub Desktop.
Abstract class to extend for Google Play Fused Location Service
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
import android.Manifest; | |
import android.content.DialogInterface; | |
import android.content.Intent; | |
import android.content.pm.PackageManager; | |
import android.location.Location; | |
import android.location.LocationListener; | |
import android.os.Bundle; | |
import android.provider.Settings; | |
import android.support.v4.app.ActivityCompat; | |
import android.support.v7.app.AlertDialog; | |
import android.util.Log; | |
import com.google.android.gms.common.ConnectionResult; | |
import com.google.android.gms.common.api.GoogleApiClient; | |
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; | |
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; | |
import com.google.android.gms.location.LocationRequest; | |
import com.google.android.gms.location.LocationServices; | |
/** | |
* Created by seb on 7/8/16. | |
*/ | |
public abstract class LocationActivity extends BaseActivity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener { | |
protected static final String TAG = "LocationActivity"; | |
// The desired interval for location updates. Inexact. Updates may be more or less frequent. | |
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000; // 10s | |
// 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 = 3000; // 3s | |
// Keys for storing activity state in the Bundle. | |
private final static String REQUESTING_LOCATION_UPDATES_KEY = "requesting-location-updates-key"; | |
private final static String LOCATION_KEY = "location-key"; | |
private final static String LAST_UPDATED_TIME_STRING_KEY = "last-updated-time-string-key"; | |
// Provides the entry point to Google Play services. | |
private GoogleApiClient mGoogleApiClient; | |
// Stores parameters for requests to the FusedLocationProviderApi. | |
private LocationRequest mLocationRequest; | |
// Represents a geographical location. | |
private Location mCurrentLocation; | |
/** | |
* Tracks the status of the location updates request. | |
*/ | |
private Boolean isRequestingLocationUpdates; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
// Update values using data stored in the Bundle. | |
updateValuesFromBundle(savedInstanceState); | |
// Kick off the process of building a GoogleApiClient and requesting the LocationServices API. | |
buildGoogleApiClient(); | |
isRequestingLocationUpdates = true; | |
startLocationUpdates(); | |
} | |
@Override | |
protected void onStart() { | |
super.onStart(); | |
mGoogleApiClient.connect(); | |
} | |
@Override | |
public void onResume() { | |
super.onResume(); | |
// Within {@code onPause()}, we pause location updates, but leave the | |
// connection to GoogleApiClient intact. Here, we resume receiving | |
// location updates if the user has requested them. | |
if (mGoogleApiClient.isConnected() && isRequestingLocationUpdates) { | |
startLocationUpdates(); | |
} | |
} | |
@Override | |
protected void onPause() { | |
super.onPause(); | |
// Stop location updates to save battery, but don't disconnect the GoogleApiClient object. | |
if (mGoogleApiClient.isConnected()) { | |
stopLocationUpdates(); | |
} | |
} | |
@Override | |
protected void onStop() { | |
mGoogleApiClient.disconnect(); | |
super.onStop(); | |
} | |
/** | |
* Updates fields based on data stored in the bundle. | |
* | |
* @param savedInstanceState The activity state saved in the Bundle. | |
*/ | |
private void updateValuesFromBundle(Bundle savedInstanceState) { | |
Log.d(TAG, "Updating values from bundle"); | |
if (savedInstanceState != null) { | |
// Update the value of isRequestingLocationUpdates from the Bundle, and make sure that | |
// the Start Updates and Stop Updates buttons are correctly enabled or disabled. | |
if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) { | |
isRequestingLocationUpdates = savedInstanceState.getBoolean( | |
REQUESTING_LOCATION_UPDATES_KEY); | |
} | |
// Update the value of mCurrentLocation from the Bundle and update the UI to show the | |
// correct latitude and longitude. | |
if (savedInstanceState.keySet().contains(LOCATION_KEY)) { | |
// Since LOCATION_KEY was found in the Bundle, we can be sure that mCurrentLocation | |
// is not null. | |
mCurrentLocation = savedInstanceState.getParcelable(LOCATION_KEY); | |
} | |
updateUserLocation(); | |
} | |
} | |
/** | |
* Builds a GoogleApiClient. Uses the {@code #addApi} method to request the | |
* LocationServices API. | |
*/ | |
private synchronized void buildGoogleApiClient() { | |
Log.i(TAG, "Building GoogleApiClient"); | |
mGoogleApiClient = new GoogleApiClient.Builder(this) | |
.addConnectionCallbacks(this) | |
.addOnConnectionFailedListener(this) | |
.addApi(LocationServices.API) | |
.build(); | |
createLocationRequest(); | |
} | |
/** | |
* Sets up the location request. Android has two location request settings: | |
* {@code ACCESS_COARSE_LOCATION} and {@code ACCESS_FINE_LOCATION}. These settings control | |
* the accuracy of the current location. This sample uses ACCESS_FINE_LOCATION, as defined in | |
* the AndroidManifest.xml. | |
* | |
* When the ACCESS_FINE_LOCATION setting is specified, combined with a fast update | |
* interval (5 seconds), the Fused Location Provider API returns location updates that are | |
* accurate to within a few feet. | |
* | |
* These settings are appropriate for mapping applications that show real-time location | |
* updates. | |
*/ | |
private void createLocationRequest() { | |
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_BALANCED_POWER_ACCURACY); // PRIORITY_HIGH_ACCURACY? | |
} | |
/** | |
* Requests location updates from the FusedLocationApi. | |
*/ | |
private void startLocationUpdates() { | |
// The final argument to {@code requestLocationUpdates()} is a LocationListener | |
// (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html). | |
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_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. | |
showAlert(); | |
return; | |
} | |
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, (com.google.android.gms.location.LocationListener) this); | |
} | |
@Override | |
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { | |
stopLocationUpdates(); | |
startLocationUpdates(); | |
} | |
/** | |
* Updates the latitude, the longitude, and the last location time in the UI. | |
*/ | |
private void updateUserLocation() { | |
Log.i(TAG, "User location has changed to [" + mCurrentLocation.getLatitude() + "," + mCurrentLocation.getLongitude() + "]"); | |
// TODO: Do stuff on location updates | |
} | |
/** | |
* Removes location updates from the FusedLocationApi. | |
*/ | |
private void stopLocationUpdates() { | |
// 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. | |
// The final argument to {@code requestLocationUpdates()} is a LocationListener | |
// (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html). | |
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, (com.google.android.gms.location.LocationListener) this); | |
} | |
/** | |
* Runs when a GoogleApiClient object successfully connects. | |
*/ | |
@Override | |
public void onConnected(Bundle connectionHint) { | |
Log.i(TAG, "Connected to GoogleApiClient"); | |
// If the initial location was never previously requested, we use | |
// FusedLocationApi.getLastLocation() to get it. If it was previously requested, we store | |
// its value in the Bundle and check for it in onCreate(). We | |
// do not request it again unless the user specifically requests location updates by pressing | |
// the Start Updates button. | |
// | |
// Because we cache the value of the initial location in the Bundle, it means that if the | |
// user launches the activity, | |
// moves to a new location, and then changes the device orientation, the original location | |
// is displayed as the activity is re-created. | |
if (mCurrentLocation == null) { | |
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { | |
showAlert(); | |
return; | |
} | |
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); | |
updateUserLocation(); | |
} | |
// If the user presses the Start Updates button before GoogleApiClient connects, we set | |
// isRequestingLocationUpdates to true (see startUpdatesButtonHandler()). Here, we check | |
// the value of isRequestingLocationUpdates and if it is true, we start location updates. | |
if (isRequestingLocationUpdates) { | |
startLocationUpdates(); | |
} | |
} | |
/** | |
* Callback that fires when the location changes. | |
*/ | |
@Override | |
public void onLocationChanged(Location location) { | |
mCurrentLocation = location; | |
updateUserLocation(); | |
} | |
@Override | |
public void onConnectionSuspended(int cause) { | |
// The connection to Google Play services was lost for some reason. We call connect() to | |
// attempt to re-establish the connection. | |
Log.i(TAG, "Connection suspended"); | |
mGoogleApiClient.connect(); | |
} | |
@Override | |
public void onConnectionFailed(ConnectionResult result) { | |
// Refer to the javadoc for ConnectionResult to see what error codes might be returned in | |
// onConnectionFailed. | |
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode()); | |
} | |
/** | |
* Stores activity data in the Bundle. | |
*/ | |
@Override | |
public void onSaveInstanceState(Bundle savedInstanceState) { | |
savedInstanceState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY, isRequestingLocationUpdates); | |
savedInstanceState.putParcelable(LOCATION_KEY, mCurrentLocation); | |
super.onSaveInstanceState(savedInstanceState); | |
} | |
private void showAlert() { | |
final AlertDialog.Builder dialog = new AlertDialog.Builder(this); | |
dialog.setTitle("Enable Location") | |
.setMessage("Your Locations Settings is set to 'Off'.\nPlease Enable Location to " + | |
"use this app") | |
.setPositiveButton("Location Settings", new DialogInterface.OnClickListener() { | |
@Override | |
public void onClick(DialogInterface paramDialogInterface, int paramInt) { | |
Intent myIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); | |
startActivity(myIntent); | |
} | |
}) | |
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { | |
@Override | |
public void onClick(DialogInterface paramDialogInterface, int paramInt) { | |
} | |
}); | |
dialog.show(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment