So, I have a JobService that is being called on background. However I need it to get the current location and send it to the webservice. The webservice is working great when I send static data. However getting the location is being a problem. The location object is always null. This is what I've been trying.
public class GPSBackgroundJobService extends JobService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private String LOGSERVICE = "GPS";
private InspectorsLogic mInspectorsLogic;
ParsoContext mContext;
private GoogleApiClient mGoogleApiClient;
ParsoLocationListener mLocationListener;
Location mLastLocation;
Double longitude;
Double latitude;
#Override
public void onCreate() {
super.onCreate();
Log.i(LOGSERVICE, "onCreate");
mContext = (ParsoContext) getApplicationContext();
mInspectorsLogic = new InspectorsLogic(mContext);
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
#Override
public boolean onStartJob(JobParameters job) {
Log.i(LOGSERVICE, "onStartJob called");
mGoogleApiClient.connect();
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation!=null) {
latitude = mLastLocation.getLatitude();
longitude =mLastLocation.getLongitude();
sendGPSTask mSendGPSTask = new sendGPSTask();
mSendGPSTask.execute(Double.toString(latitude), Double.toString(longitude));
}else{
Log.e(LOGSERVICE, "Lat and Long are null");
}
return false;
}
#Override
public boolean onStopJob(JobParameters job) {
mGoogleApiClient.disconnect();
return false;
}
#Override
public void onConnected(Bundle connectionHint) {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
}
getLastLocation returns null if it doesn't have a location fix yet- which means unless you're using GPS elsewhere, it almost always returns null. To make sure you get a location use getLocationUpdates and wait for the location to fix (this will not be instant, depending on atmospheric conditions, whether you're inside or out, etc this could take a few seconds to a minute or so- or just never happen).
Apparently the code is fine, to make it work this is what I did:
check the permissions of the app (the location one needs to be activated)
change your GPS settings to (mobile data and wifi one)
making this with the current code posted on the question, made it.
Related
I'm developing an android application to track user location in background and keep updating it to firebase database as location changes the app is working fine when the application is opened but when i close it , firebase stop setting new values , it will set the new values directly after I open the application again
this is my android service code :
public class LocationServiceFB extends Service implements LocationListener,GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener{
private Location mLastLocation;
DatabaseReference instructors;
GeoFire geofire;
String UserId;
SharedPreferences pref;
GoogleApiClient mGoogleApiClient;
LocationRequest mLocationRequest;
private static int UPDATE_INTERVAL = 5000;
private static int DISPLACEMENT = 10;
private static int FATEST_INTERVAL = 3000;
private LocationManager mLocationManager;
#Nullable
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not Yet Implemented");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
instructors = FirebaseDatabase.getInstance().getReference("OnlineInstructors");
geofire = new GeoFire(instructors);
pref = this.getSharedPreferences("LoginTrack", Context.MODE_PRIVATE);
UserId = pref.getString("firebasekey","");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
instructors.child(UserId).removeValue();
}
#Override
public void onConnected(#Nullable Bundle bundle) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FATEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED){
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,mLocationRequest,this);
}
#Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
#Override
public void onLocationChanged(Location location) {
mLastLocation = location;
geofire.setLocation(UserId, new GeoLocation(location.getLatitude(), location.getLongitude()), new GeoFire.CompletionListener() {
#Override
public void onComplete(String key, DatabaseError error) {
Log.d("ERROR" , "INSTRUCTOR LOCATION SENT TO DATABASE" );
}
});
}
public void uploadLocation(){
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Log.d("EER","np permissions");
return;
}
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
final double latitude = mLastLocation.getLatitude();
final double longitude = mLastLocation.getLongitude();
geofire.setLocation(UserId, new GeoLocation(latitude, longitude), new GeoFire.CompletionListener() {
#Override
public void onComplete(String key, DatabaseError error) {
Log.d("ERROR" , "INSTRUCTOR LOCATION SENT TO DATABASE" );
}
});
}
}
Android may kill your app's process in favor of more important apps. Android may also stop your process from networking when it's not longer visible in the foreground. This is to prevent poorly behaved apps from consuming too many resources.
If your apps needs to continue networking when the user is no longer using it, you'll have to start a foreground service, which also requires that you show a notification that indicates to the user that your app is still running.
You are using a bound service for upadating your position to the server.
Your service gets killed along with app.
Use an unbound service. Unbound service keeps running in the background even when the app is killed. So, this will work fine even in the background with unbound service.
This question may be asked many time but I couldn't find any solution for it.
I need to get the user's location in background for ever 15 min.
It works fine when the GPS is turned on but when the GPS is turned off, the app is not getting location updates. I need it to get at least the network location or wifi location.
How can I achieve this with fused location API.?
Location manager has this functionality but in fused location. Because Google suggests developers use Location from the play services API, I don't want to use location manager.
How can I achieve this so let me post what I tried so far:
import android.content.Context;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.FusedLocationProviderApi;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
/**
* Created by 4264 on 14-10-2016.
*/
public class Locationlistener implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,LocationListener {
private Location mlocation; // location
private double latitude; // latitude
private double longitude; // longitude
private GoogleApiClient mGAC;
private Context mContext;
public static final String TAG = "GPSresource";
private FusedLocationProviderApi fusedLocationProviderApi;
private LocationRequest locationRequest;
public Locationlistener(Context c)
{
mContext = c;
try {
buildGoogleApiClient();
mGAC.connect();
}
catch(Exception e)
{
Log.d(TAG,e.toString());
}
}
protected synchronized void buildGoogleApiClient() {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER);
locationRequest.setInterval(1);
locationRequest.setFastestInterval(1);
mGAC = new GoogleApiClient.Builder(mContext)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
public double getLatitude(){
if(mlocation != null){
latitude = mlocation.getLatitude();
}
// return latitude
return latitude;
}
/**
* Function to get longitude
* */
public double getLongitude() {
if (mlocation != null) {
longitude = mlocation.getLongitude();
}
// return longitude
return longitude;
}
public Location GetLocationBlocking() throws InterruptedException {
// String lat=String.valueOf(moCurrentLocation.getLatitude());
// String longt=String.valueOf(moCurrentLocation.getLongitude());
// Toast.makeText(oContext,"Lat"+lat+"long"+longt,Toast.LENGTH_SHORT).show();
return mlocation;
}
#Override
public void onConnected(Bundle bundle) {
Location oLocation = LocationServices.FusedLocationApi.getLastLocation(mGAC);
if (mGAC != null) {
mlocation = oLocation;
getLatitude();
getLongitude();
if (oLocation != null){
Log.d("lat",String.valueOf(mlocation.getLatitude()));
Log.d("long",String.valueOf(mlocation.getLongitude()));
}
else{
LocationServices.FusedLocationApi.requestLocationUpdates(mGAC, locationRequest, this);
} }}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
#Override
public void onLocationChanged(Location location) {
mlocation=location;
}
}
i don't understand when you saying you don't want use location manager and GPS...look at this Location Strategies... based on this doc we have 3 strategy to access location:
Gps
Network Location Provider
or using both
but for your location problem this is my offer:
Inside an Activity, put the following to connect and start receiving location updates:
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private long UPDATE_INTERVAL = 10 * 1000; /* 10 secs */
private long FASTEST_INTERVAL = 2000; /* 2 sec */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create the location client to start receiving updates
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
}
protected void onStart() {
super.onStart();
// Connect the client.
mGoogleApiClient.connect();
}
protected void onStop() {
// Disconnecting the client invalidates it.
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
// only stop if it's connected, otherwise we crash
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onStop();
}
public void onConnected(Bundle dataBundle) {
// Get last known recent location.
Location mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
// Note that this can be NULL if last location isn't already known.
if (mCurrentLocation != null) {
// Print current location if not null
Log.d("DEBUG", "current location: " + mCurrentLocation.toString());
LatLng latLng = new LatLng(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
}
// Begin polling for new location updates.
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
if (i == CAUSE_SERVICE_DISCONNECTED) {
Toast.makeText(this, "Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
} else if (i == CAUSE_NETWORK_LOST) {
Toast.makeText(this, "Network lost. Please re-connect.", Toast.LENGTH_SHORT).show();
}
}
// Trigger new location updates at interval
protected void startLocationUpdates() {
// Create the location request
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(UPDATE_INTERVAL)
.setFastestInterval(FASTEST_INTERVAL);
// Request location updates
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
}
and then register for location updates with onLocationChanged:
public void onLocationChanged(Location location) {
// New location has now been determined
String msg = "Updated Location: " +
Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
// You can now create a LatLng Object for use with maps
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
}
For more information on the Fused Location API, refer to Make ypur app Location Aware
Troubleshooting Location Updates
Location updates should always be done using the GoogleApiClient leveraging the LocationServices.API as shown above. Do not use the older Location APIs which are much less reliable. Even when using the correct FusedLocationApi, there are a lot of things that can go wrong. Consider the following potential issues:
Did you add the necessary permissions? Make sure your app has
INTERNET and ACCESS_COARSE_LOCATION permissions to ensure that
location can be accessed as illustrated in the guide above.
Are you getting null when calling
LocationServices.FusedLocationApi.getLastLocation? This is normal
since this method only returns if there is already a location
recently retrieved by another application. If this returns null, this
means you need start receiving location updates with
LocationServices.FusedLocationApi.requestLocationUpdates before
receiving the location as shown above.
Are you trying to get location on the genymotion emulator? Ensure
you've enabled GPS and configured a lat/lng properly. Try restarting
the emulator if needed and re-enabling GPS or trying a device (or the
official emulator) instead to rule out genymotion specific issues.
I have used the Fused Location Provider API to get user's current location.Things work well when the user has turned location on.
But I want to provide a message to the user if he has turned location off. When location is turned off, the requestLocationUpdates does not call onLocationChanged method . May be this is expected, but i would like to know what is called when location is turned off so that i can capture it to inform the user. Below is the code I am using (picked from an example in Stackoverflow, the link to which is lost from me).
public class FusedLocationService implements
LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = "FusedLocationService";
private static final long INTERVAL = 1000 * 30;
private static final long FASTEST_INTERVAL = 1000 * 5;
private static final long ONE_MIN = 1000 * 60;
private static final long REFRESH_TIME = ONE_MIN * 5;
private static final float MINIMUM_ACCURACY = 50.0f;
Activity locationActivity;
private LocationRequest locationRequest;
private GoogleApiClient googleApiClient;
private Location location;
private FusedLocationProviderApi fusedLocationProviderApi = LocationServices.FusedLocationApi;
FusedLocationReceiver locationReceiver = null;
public FusedLocationService(Activity locationActivity, FusedLocationReceiver locationReceiver) {
this.locationReceiver = locationReceiver;
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(INTERVAL);
locationRequest.setFastestInterval(FASTEST_INTERVAL);
this.locationActivity = locationActivity;
googleApiClient = new GoogleApiClient.Builder(locationActivity)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
if (googleApiClient != null) {
googleApiClient.connect();
}
}
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "I'm connected now");
Location currentLocation = fusedLocationProviderApi.getLastLocation(googleApiClient);
if (currentLocation != null && currentLocation.getTime() > REFRESH_TIME) {
location = currentLocation;
} else {
fusedLocationProviderApi.requestLocationUpdates(googleApiClient, locationRequest, this);
// Schedule a Thread to unregister location listeners
Executors.newScheduledThreadPool(1).schedule(new Runnable() {
#Override
public void run() {
fusedLocationProviderApi.removeLocationUpdates(googleApiClient,
FusedLocationService.this);
}
}, ONE_MIN, TimeUnit.MILLISECONDS);
}
}
#Override
public void onLocationChanged(Location location) {
Log.i(TAG, "Location is changed!");
//if the existing location is empty or
//the current location accuracy is greater than existing accuracy
//then store the current location
if (null == this.location || location.getAccuracy() < this.location.getAccuracy()) {
this.location = location;
// let's inform my client class through the receiver
locationReceiver.onLocationChanged();
//if the accuracy is not better, remove all location updates for this listener
if (this.location.getAccuracy() < MINIMUM_ACCURACY) {
fusedLocationProviderApi.removeLocationUpdates(googleApiClient, this);
}
}
}
public Location getLocation() {
return this.location;
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
}
I think you can just use the LocationManager per this question: How to check if Location Services are enabled?
Please go through the SettingApi, which you can use to display dialog.
https://developer.android.com/reference/com/google/android/gms/location/SettingsApi.html
Skim Location settings dialog. Here you can find what you have to do with the dialog.
http://android-developers.blogspot.in/2015/03/google-play-services-70-places-everyone.html
I develop an application which needs to be location aware. I tried to use the new google api client but i have some problems.
1.When i ask for location I ask for PRIORITY_HIGH_ACCURACY but i want the user to have the choise to save his battery by using only the mobile networks. When I check Battery saving mode (or disable GPS on old devices) everything works fine. But when I tested the app on a phone with lollipop, even if I close all location providers, the battery is killed. (I also noticed that and by the time the battery is drained, no location update received).
2.I tested to some devices and tried to get location with mobile networks. That worked fine when the device was close to a wifi (but not connected). But in a specific device (sony xperia M2) it is needed to connect to wifi to update location. Is there anything i can do? Is problem of the device or the version of google play services?
My code is here. I fire periodically a pending intent to run the service to be sure that location listener is still listening (i had some problems in the past where location listener disconnected)
public class UpdateLocService extends Service implements GoogleApiClient.ConnectionCallbacks, LocationListener {
public static final String SENDER_ID = "66801";
public static final String devIdToServer = "send dev id to server";
private static LocationListener listener = null;
//private LocationClient locationClient;
private GoogleApiClient mGoogleApiClient;
private static Location lastLocation = null;;
private LocationRequest mLocationRequest;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
#Deprecated
public void onStart(Intent intent, int startId) {
Log.d("UpdateLocService", "dienem service ");
locApi();
}
private void locApi() {
if(mGoogleApiClient != null && (mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting())){
dLog.d("loc.txt","tryed to connect but already connected or trying now");
dLog.d("flow.txt","tryed to connect but already connected or trying now");
}else{
dLog.d("loc.txt","new connection attempt");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
//.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(1000 * 60);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
}
public static Location getLocation(){
return lastLocation;
}
#Override
public void onLocationChanged(Location location) {
dLog.d("loc.txt",location.toString());
lastLocation = location;
}
}
I have created the below code to simply get the most recent latitude and longitude without needing the user to turn on GPS but simply rely on wifi or network for positioning. Upon running the app, it simply returns the toast "The Lat is 0.0 and Long is 0.0". I know this should work because I have downloaded the sample code here: https://github.com/googlesamples/android-play-location/tree/master/BasicLocationSample, and adapted to work in my code.
How can I get this to work?
public class MainActivity extends FragmentActivity implements GoogleApiClient.ConnectionCallbacks, OnConnectionFailedListener {
public static double lat;
public static double lng;
ViewPager viewPager = null;
/**
* Provides the entry point to Google Play services.
*/
protected GoogleApiClient mGoogleApiClient;
/**
* Represents a geographical location.
*/
protected Location mLastLocation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buildGoogleApiClient();
String text = "The Lat is " + lat
+ " and Long is " + lng;
Toast.makeText(getBaseContext(), text, Toast.LENGTH_LONG).show();
};
// Assign list to actionbar
actionBar.setListNavigationCallbacks(aAdpt, null); // DEPRACATED
}
/**
* Builds a GoogleApiClient. Uses the addApi() method to request the LocationServices API.
*/
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
#Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
#Override
protected void onStop() {
super.onStop();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
/**
* Runs when a GoogleApiClient object successfully connects.
*/
#Override
public void onConnected(Bundle connectionHint) {
// Provides a simple way of getting a device's location and is well suited for
// applications that do not require a fine-grained location and that do not need location
// updates. Gets the best and most recent location currently available, which may be null
// in rare cases when a location is not available.
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation != null) {
lat = mLastLocation.getLatitude();
lng = mLastLocation.getLongitude();
} else {
Toast.makeText(this,"No location detected", Toast.LENGTH_LONG).show();
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Refer to the javadoc for ConnectionResult to see what error codes might be returned in
// onConnectionFailed.
}
#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.
mGoogleApiClient.connect();
}
}
}
"lat", "lng", and "mLastLocation" isn't set or valid until onConnected() has been executed. You need to understand that this operation is asynchronous -- that is, the location doesn't come back immediately but is provided later on. This would have been obvious if you tested whether "mLastLocation" was null or not when you made that toast.