Reputation: 607
Am working on an app that needs location update frequently even when its in the background. Following the documentation here, am working with pending intent not locationlistener. My code is below
/**
* Created by philip on 7/30/16.
*/
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000 * 30; //1 minute;
/**
* 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;
private String mLastUpdateTime;
private Location mCurrentLocation;
private GeoFire mGeoFire = null;
private String uuid = null;
private Intent intentService;
private PendingIntent mPendingIntent;
@Override
public void onCreate() {
super.onCreate();
Firebase.setAndroidContext(this);
buildGoogleApiClient();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!mGoogleApiClient.isConnected()) {
mGoogleApiClient.connect();
}
createLocationRequest();
if(intent != null){
intentService = new Intent(this, LocationBroadcastReceiver.class);
intentService.putExtra("UUID", intent.getStringExtra("UUID"));
intentService.setAction("foo.LOCATION_UPDATE_INTENT");
mPendingIntent = PendingIntent.getBroadcast(this, 0, intentService, PendingIntent.FLAG_UPDATE_CURRENT);
}
return START_REDELIVER_INTENT;
}
/********************************************** Google api connection callback below **************************************/
/***
* callback fired once connection has been established
* @param bundle
*/
@Override
public void onConnected(Bundle bundle) {
startLocationUpdates();
Log.i(this.getClass().getSimpleName(), "Location Service api has been connected");
}
/***
* callback fired when connection fails
* @param connectionResult
*/
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.i(this.getClass().getSimpleName(), "Location Service disconnected");
}
/**
* callback fired when connection is temporary suspended
* @param i
*/
@Override
public void onConnectionSuspended(int i) {
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
}
/************************************** Ease methods ************************************************/
/**
* Builds a GoogleApiClient. Uses the {@code #addApi} method to request the
* LocationServices API.
*/
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(API)
.build();
}
/**
* Requests location updates from the FusedLocationApi.
*/
protected void startLocationUpdates() {
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
return;
}
FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mPendingIntent);
}
protected 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_HIGH_ACCURACY);
}
}
and my broadcastreceiver
@Override
public void onReceive(Context context, Intent intent) {
Firebase.setAndroidContext(context);
Log.i(getClass().getSimpleName(), "broadcast has been called");
if (intent != null) {
String uuid = intent.getStringExtra("UUID");
mGeoFire = new GeoFire(new Firebase("https://foo.firebaseio.com/users/" + uuid));
LocationResult locationResult = LocationResult.extractResult(intent);
if(locationResult == null){// check for null pointer
Log.i(getClass().getSimpleName(), "location result is null <<<<<<<<<<<<<<<<<<");
return;
}
Log.i(getClass().getSimpleName(), "location result found >>>>>>>>>>>>>>>>>>>>====>>>>>>>>>>>>>>>>>>>");
location = locationResult.getLastLocation();
}
if( isBetterLocation(location, currentBestLocation)){
currentBestLocation = location;
}else {
return;
}
if (mGeoFire != null) {
mGeoFire.setLocation("location", new GeoLocation(location.getLatitude(), location.getLongitude()));
}
}
The issue i face is that
LocationResult locationResult = LocationResult.extractResult(intent);
returns null
and that is the last i hear from my broadcast reveiver
Upvotes: 1
Views: 1487
Reputation: 607
I basically moved my listener to onConnected
and checked for null
. It worked fine after then.
package com.github.robophil.location_service;
import android.Manifest;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.widget.Toast;
import com.firebase.client.Firebase;
import com.firebase.geofire.GeoFire;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import static com.google.android.gms.location.LocationServices.API;
import static com.google.android.gms.location.LocationServices.FusedLocationApi;
/**
* Created by philip on 7/30/16.
*/
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
public static final String prefName = "com.github.robophil.pref";
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000 * 60; //1 minute;
/**
* 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;
private String mLastUpdateTime;
private Location mCurrentLocation;
private GeoFire mGeoFire = null;
private String uuid = null;
private Intent intentService;
private PendingIntent mPendingIntent;
@Override
public void onCreate() {
super.onCreate();
Firebase.setAndroidContext(this);
buildGoogleApiClient();
}
@Override
public void onDestroy() {
if(mGoogleApiClient != null){
if(mGoogleApiClient.isConnected()){
mGoogleApiClient.disconnect();
FusedLocationApi.removeLocationUpdates(mGoogleApiClient, mPendingIntent);
}
}
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
if(!intent.hasExtra("UUID") || !intent.hasExtra("URL")){
Log.i(getClass().getSimpleName(), "Service has stopped itself");
Toast.makeText(this, "stopself called", Toast.LENGTH_SHORT).show();
stopSelf();
}
SharedPreferences.Editor editor = getSharedPreferences(LocationService.prefName, getApplicationContext().MODE_PRIVATE).edit();
editor.putString("UUID", intent.getStringExtra("UUID"));
editor.putString("URL", intent.getStringExtra("URL"));
editor.apply();
intentService = new Intent(this, LocationIntentService.class);
intentService.setExtrasClassLoader(LocationResult.class.getClassLoader());
mPendingIntent = PendingIntent.getService(getApplicationContext(), 0, intentService, PendingIntent.FLAG_UPDATE_CURRENT);
}
if (!mGoogleApiClient.isConnected()) {
mGoogleApiClient.connect();
}
return START_REDELIVER_INTENT;
}
/********************************************** Google api connection callback below **************************************/
/***
* callback fired once connection has been established
* @param bundle
*/
@Override
public void onConnected(Bundle bundle) {
createLocationRequest();
startLocationUpdates();
Log.i(this.getClass().getSimpleName(), "Location Service api has been connected");
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
return;
}
Location location = FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location == null){
Log.i(getClass().getSimpleName(), "init location is null");
return;
}
Log.i(getClass().getSimpleName(), "init location ==> "+location.getAccuracy()+" "+location.getProvider());
}
/***
* callback fired when connection fails
* @param connectionResult
*/
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.i(this.getClass().getSimpleName(), "Location Service disconnected");
}
/**
* callback fired when connection is temporary suspended
* @param i
*/
@Override
public void onConnectionSuspended(int i) {
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
}
/************************************** Ease methods ************************************************/
/**
* Builds a GoogleApiClient. Uses the {@code #addApi} method to request the
* LocationServices API.
*/
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(API)
.build();
}
/**
* Requests location updates from the FusedLocationApi.
*/
protected void startLocationUpdates() {
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
return;
}
// FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mPendingIntent);
}
protected 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_HIGH_ACCURACY);
}
}
LocationsIntentService used for my pending intent stays the same, just checking for null before doing anything
package com.github.robophil.location_service;
import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import com.firebase.client.Firebase;
import com.firebase.geofire.GeoFire;
import com.firebase.geofire.GeoLocation;
import com.google.android.gms.location.LocationResult;
import java.util.Set;
/**
* Created by philip on 8/7/16.
*/
public class LocationIntentService extends IntentService {
private GeoFire mGeoFire;
private Location location, currentBestLocation;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public LocationIntentService(String name) {
super("i am a value");
}
public LocationIntentService(){
super("i am a value");
}
@Override
protected void onHandleIntent(Intent intent) {
Firebase.setAndroidContext(this);
Log.i(getClass().getSimpleName(), "intent service has been called");
SharedPreferences pref = getSharedPreferences(LocationService.prefName, getApplicationContext().MODE_PRIVATE);
String uuid = pref.getString("UUID", null);
String url = pref.getString("URL", null);
if(uuid == null || url==null){
stopSelf();
return;
}
if (intent != null) {
mGeoFire = new GeoFire(new Firebase(url +"/"+ uuid));
Log.i(getClass().getSimpleName(), "user id found ==> "+uuid+", "+url);
Bundle extra = intent.getExtras();
Set<String> extraKeySet = extra.keySet();
for(String key: extraKeySet){
Log.i(getClass().getSimpleName(), "key found ==> "+ key);
}
if(LocationResult.hasResult(intent)){
Log.i(getClass().getSimpleName(), "intent contains location");
}
LocationResult locationResult = LocationResult.extractResult(intent);
if(locationResult == null){// check for null pointer
Log.i(getClass().getSimpleName(), "location result is null ...");
return;
}
Log.i(getClass().getSimpleName(), "location result found");
location = locationResult.getLastLocation();
}
if( isBetterLocation(location, currentBestLocation)){
currentBestLocation = location;
}else {
return;
}
if (mGeoFire != null) {
mGeoFire.setLocation("location", new GeoLocation(location.getLatitude(), location.getLongitude()));
}
}
private static final int TWO_MINUTES = 1000 * 60 * 2;
/** Determines whether one Location reading is better than the current Location fix
* @param location The new Location that you want to evaluate
* @param currentBestLocation The current Location fix, to which you want to compare the new one
*/
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
}
Upvotes: 1