This is kinda a repost and I'm sorry for that, but the last time I posted it I thought I got it down but apparently not. When I tried to call the Service I use to get this error Caused by: java.lang.IllegalStateException: GoogleApiClient is not connected yet.
Because I was trying to call LocationServices before GoogleApiClient was even connected. So after changing the code up a bit I wasn't getting the error anymore, in fact I wasn't getting anything in logcat anymore.
This is how I start my Service from SearchActivity.class:
SearchActivity.class
button = (Button)findViewById(R.id.buttonPressed);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startService(new Intent(getBaseContext(), LocationService.class));
}
});
This is the Service:
LocationService.class
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener {
// LogCat tag
private static final String TAG = LocationService.class.getSimpleName();
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 1000;
private Location mLastLocation;
// Google client to interact with Google API
private GoogleApiClient mGoogleApiClient;
// boolean flag to toggle periodic location updates
private boolean mRequestingLocationUpdates = false;
private LocationRequest mLocationRequest;
// Location updates intervals in sec
private static int UPDATE_INTERVAL = 10000; // 10 sec
private static int FATEST_INTERVAL = 5000; // 5 sec
private static int DISPLACEMENT = 10; // 10 meters
#Override
public void onCreate() {
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mGoogleApiClient != null) {
if(mGoogleApiClient.isConnected()) {
if (mLocationRequest != null) {
togglePeriodicLocationUpdates();
}
}
}
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FATEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
}
private void togglePeriodicLocationUpdates() {
mGoogleApiClient.connect();
if(mGoogleApiClient.isConnected()) {
if (!mRequestingLocationUpdates) {
mRequestingLocationUpdates = true;
startLocationUpdates();
Log.d(TAG, "Periodic location updates started!");
} else {
mRequestingLocationUpdates = false;
// Stopping the location updates
stopLocationUpdates();
Log.d(TAG, "Periodic location updates stopped!");
}
}
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
}
protected void startLocationUpdates() {
mGoogleApiClient.connect();
if(mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
}
#Override
public void onConnected(Bundle arg0) {
createLocationRequest();
}
#Override
public void onConnectionSuspended(int arg0) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = "
+ result.getErrorCode());
}
#Override
public void onLocationChanged(Location location) {
// Assign the new location
mLastLocation = location;
Toast.makeText(getApplicationContext(), "Location changed!",
Toast.LENGTH_SHORT).show();
}
#Override
public boolean stopService(Intent name) {
return super.stopService(name);
}
AndroidManifest.xml
</activity>
<service android:name=".LocationService">
</service>
Edit:
Well I figured how to get it working.
But now I'm facing the problem of having to click the button 2 times before it would start normally. Only had to change the onStartCommand() a bit.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
if(mGoogleApiClient.isConnected()) {
if (mLocationRequest != null) {
togglePeriodicLocationUpdates();
}
}
}
return START_NOT_STICKY;
}
You should read the documentation on GoogleApiClient, particularly (for the question at hand) the connect() method. I've highlighted the relevant portion:
Connects the client to Google Play services. This method returns
immediately, and connects to the service in the background. If the
connection is successful, onConnected(Bundle) is called and enqueued
items are executed. On a failure, onConnectionFailed(ConnectionResult)
is called.
If the client is already connected or connecting, this method does
nothing.
So, this is what currently happens with your code:
You click the button to launch the service.
You create the GoogleApiClient in onCreate().
You call connect() in onStartCommand which returns immediately while connection goes on in the background.
You check isConnected(), however, since connection is still going on, you do nothing and return from onStartCommand.
Later, you click the button the launch the service again.
As it has already been created, it goes directly to onStartCommand.
By this point, the GoogleApiClient has connected, so everything else works as expected.
What you should do is implement onConnected(). From there, call togglePeriodicLocationUpdates() instead of doing that in onStartCommand(). This will accomplish what you want:
Click button to launch service.
Service creates GoogleApiClient in onCreate().
Service initiates connection in onStartCommand().
Sometime in the future, the connection is established and service calls togglePeriodicLocationUpdates() from onConnected().
Related
I am working on a new open source library, called Galileo, that helps developers using location api from Google in a single line.
However, when I resume my app, in OnConnected I get the exception that the client is not yet connected. The problem is, that using their methods, everything works fine, but I can't get to work my implementation. Here is what I got so far:
Galileo.java:
public class Galileo {
static volatile Galileo singleton;
private GoogleApiClient mGoogleApiClient;
private LocationManager locationManager;
private LocationListener locationListener;
Galileo (GoogleApiClient mGoogleApiClient, LocationListener locationListener){
this.mGoogleApiClient = mGoogleApiClient;
this.locationListener = locationListener;
}
public static Galileo with(#NonNull GoogleApiClient mGoogleApiClient, #NonNull LocationListener locationListener ) {
if (mGoogleApiClient == null || locationListener==null) {
throw new IllegalArgumentException("context == null");
}
if (singleton == null) {
synchronized (Galileo.class) {
if (singleton == null) {
singleton = new Builder(mGoogleApiClient, locationListener).build();
}
}
}
return singleton;
}
public LocationRequest load(){
return new LocationRequest(this);
}
LocationListener getLocationListener(){
return this.locationListener;
}
GoogleApiClient getmGoogleApiClient(){
return this.mGoogleApiClient;
}
public static class Builder {
private final GoogleApiClient mGoogleApiClient;
private final LocationListener locationListener;
public Builder(#NonNull GoogleApiClient mGoogleApiClient, #NonNull LocationListener locationListener) {
if (mGoogleApiClient == null || locationListener == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.mGoogleApiClient = mGoogleApiClient;
this.locationListener = locationListener;
}
/**
* Toggle whether debug logging is enabled.
* <p>
* <b>WARNING:</b> Enabling this will result in excessive object allocation. This should be only
* be used for debugging purposes. Do NOT pass {#code BuildConfig.DEBUG}.
*/
public Galileo build() {
GoogleApiClient mGoogleApiClient = this.mGoogleApiClient;
LocationListener locationListener = this.locationListener;
return new Galileo(mGoogleApiClient, locationListener);
}
}
}
LocationRequest.java:
public class LocationRequest {
private LocationListener locationListener;
private GoogleApiClient mGoogleApiClient;
private float mDistance;
private int mTime;
private int priority;
private boolean askForGPS;
private com.google.android.gms.location.LocationRequest mLocationRequest;
LocationRequest(Galileo galileo) {
this.mGoogleApiClient = galileo.getmGoogleApiClient();
this.locationListener = galileo.getLocationListener();
this.mTime = 10000;
this.priority = com.google.android.gms.location.LocationRequest.PRIORITY_HIGH_ACCURACY;
this.askForGPS = false;
}
public LocationRequest time(int mTime){
this.mTime = mTime;
return this;
}
public LocationRequest distance(float mDistance){
this.mDistance = mDistance;
return this;
}
public void go(){
createLocationRequest();
startLocationUpdates();
}
public void stop(){
stopLocationUpdates();
}
protected 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, locationListener);
}
protected void startLocationUpdates() {
// The final argument to {#code requestLocationUpdates()} is a LocationListener
// (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html).
System.out.print(mGoogleApiClient.isConnected());
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest , locationListener);
}
protected void createLocationRequest() {
mLocationRequest = new com.google.android.gms.location.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(mTime);
// 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(mTime/2);
mLocationRequest.setPriority(priority);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true);
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(#NonNull LocationSettingsResult result) {
final Status status = result.getStatus();
final LocationSettingsStates state = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can initialize location
// requests here.
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
/*try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
//status.startResolutionForResult(MapsActivity.this, 1000);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}*/
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way to fix the
// settings so we won't show the dialog.
break;
}
}
});
}
}
and MapsActivity.java:
protected synchronized void buildGoogleApiClient() {
Log.i(TAG, "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
//createLocationRequest();
}
protected 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, this);
}
protected void startLocationUpdates() {
// The final argument to {#code requestLocationUpdates()} is a LocationListener
// (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html).
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
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(10000);
// 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(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true);
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(#NonNull LocationSettingsResult result) {
final Status status = result.getStatus();
final LocationSettingsStates state = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can initialize location
// requests here.
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(
MapsActivity.this, 1000);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way to fix the
// settings so we won't show the dialog.
break;
}
}
});
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(48.162253, 17.0463122), 15));
MarkerOptions marker = new MarkerOptions().position(new LatLng(48.162253, 17.0463122)).title("Hello Maps ");
mMap.addMarker(marker);
mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
behavior.setState(BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED);
String imageURL = "https://maps.googleapis.com/maps/api/streetview?size=400x400&location="+String.valueOf(marker.getPosition().latitude)+","+String.valueOf(marker.getPosition().longitude);
Picasso.with(MapsActivity.this).load(imageURL).into(imageView);
bottomSheetTextView.setText(marker.getTitle());
currentMarker = marker;
Geocoder geocoder;
List<Address> addresses;
String address;
geocoder = new Geocoder(MapsActivity.this, Locale.getDefault());
try {
addresses = geocoder.getFromLocation(marker.getPosition().latitude, marker.getPosition().longitude, 1); // Here 1 represent max location result to returned, by documents it recommended 1 to 5
address = addresses.get(0).getAddressLine(0);
} catch (IOException e) {
address = "";
e.printStackTrace();
}
addressView.setText(address);
return false;
}
});
mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
#Override
public void onMapClick(LatLng latLng) {
if (behavior.getState() == BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED) {
behavior.setState(BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN);
}
currentMarker = null;
}
});
mMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
#Override
public void onMapLongClick(LatLng latLng) {
MarkerOptions marker = new MarkerOptions().position(latLng).title("Hello Maps ");
mMap.addMarker(marker);
}
});
}
#Override
protected void onStart() {
super.onStart();
String[] LOCATION_PERMISSIONS = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
Permission.PermissionBuilder permissionBuilder =
new Permission.PermissionBuilder(LOCATION_PERMISSIONS, 1000, new Permission.PermissionCallback() {
#Override
public void onPermissionGranted(int i) {
buildGoogleApiClient();
mGoogleApiClient.connect();
}
#Override
public void onPermissionDenied(int i) {
}
#Override
public void onPermissionAccessRemoved(int i) {
}
});
requestAppPermissions(permissionBuilder.build());
}
#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 != null && mGoogleApiClient.isConnected()) {
Galileo.with(mGoogleApiClient,this).load().go();
}
}
#Override
protected void onPause() {
super.onPause();
// Stop location updates to save battery, but don't disconnect the GoogleApiClient object.
if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
Galileo.with(mGoogleApiClient,this).load().stop();
}
}
#Override
protected void onStop() {
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onStop();
}
#Override
public void onConnected(#Nullable Bundle bundle) {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if(mLastLocation!=null){
Log.d("Lat, lon", String.valueOf(mLastLocation.getLatitude()) + " ," + String.valueOf(mLastLocation.getLongitude()));
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()), 15));
}
//Log.d(TAG, String.valueOf(mGoogleApiClient.isConnected()));
if(mGoogleApiClient.isConnected()){
Galileo.with(mGoogleApiClient,this).load().go();}
}
#Override
public void onConnectionSuspended(int i) {
Log.i(TAG, "Connection suspended");
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode());
}
#Override
public void onLocationChanged(Location location) {
if(mLastLocation==null){
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 15));
}
mLastLocation = location;
Log.d("Lat, lon", String.valueOf(mLastLocation.getLatitude()) + " ," + String.valueOf(mLastLocation.getLongitude()));
//mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()), 15));
}
The thing is that I'm trying on the emulator, and it works but only once I press the "send location to device" on the emulator but before that I don't have the chance to get the location....How do I get the location for the first time using this service?. I don't have a real device right know and the emulator is my only way of testing this for now.....I tried to send a 0,0 at first to see if that would trigger a location update but it's not working as well......any ideas?
#SuppressWarnings("MissingPermission")
public class GPSService extends Service {
private LocationListener listener;
private LocationManager locationManager;
private String lastKnownLatitude;
private String lastKnownLongitude;
private Boolean isFirstTime=true;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
broadcastLocation("0","0");
listener= new LocationListener() {
#Override
public void onLocationChanged(Location location) {
//To transfer the data to the main activity I use broadcast receiver in the main activity, using an intent filter location_update
Intent intentSendLocationMainActivity = new Intent("location_update");
lastKnownLatitude=""+location.getLatitude();
lastKnownLongitude=""+location.getLongitude();
Log.d("LOCATION-UPDATE",lastKnownLatitude+" long:"+lastKnownLongitude);
broadcastLocation(lastKnownLatitude,lastKnownLongitude);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
Log.d("GPS-Stat-Changed",s);
}
#Override
public void onProviderEnabled(String s) {
Log.d("GPS-Provider-Enabled",s);
}
#Override
public void onProviderDisabled(String s) {
Intent activateGPSIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
activateGPSIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activateGPSIntent);
}
};
locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
//noinspection MissingPermission, listen for updates every 3 seconds
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000,0,listener);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ClearFromRecentService", "Service Started");
Log.d("LAST_LAT_AND_LONG",lastKnownLatitude+" "+lastKnownLongitude);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("ClearFromRecentService", "Service Destroyed, Removing update location listener");
//unregistering the listener
/*if(locationManager != null){
locationManager.removeUpdates(listener);
}*/
}
public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearFromRecentService", "END");
//here you can call a background network request to post you location to server when app is killed
Toast.makeText(getApplicationContext(), "I'm still getting user coordinates", Toast.LENGTH_LONG).show();
//stopSelf(); //call this method to stop the service
}
public void broadcastLocation(String latitude,String longitude){
Intent intentSendLocationMainActivity = new Intent("location_update");
intentSendLocationMainActivity.putExtra("latitude",latitude);
intentSendLocationMainActivity.putExtra("longitude",longitude);
//I need to differentiate here if the app is killed or not to send the location to main activity or to a server
sendBroadcast(intentSendLocationMainActivity);
}
}
EDIT: This is the complete service working. For the first time it gets the coordinates with the getLastKnownLocation() and the on sucesive times with the listener onLocationChanged()
#SuppressWarnings("MissingPermission")
public class GPSService extends Service {
private LocationListener listener;
private LocationManager locationManager;
private String lastKnownLatitude;
private String lastKnownLongitude;
private Boolean isFirstTime=true;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
listener= new LocationListener() {
#Override
public void onLocationChanged(Location location) {
//To transfer the data to the main activity I use broadcast receiver in the main activity, using an intent filter location_update
Intent intentSendLocationMainActivity = new Intent("location_update");
lastKnownLatitude=""+location.getLatitude();
lastKnownLongitude=""+location.getLongitude();
Log.d("LOCATION-UPDATE",lastKnownLatitude+" long:"+lastKnownLongitude);
broadcastLocation(lastKnownLatitude,lastKnownLongitude);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
Log.d("GPS-Stat-Changed",s);
}
#Override
public void onProviderEnabled(String s) {
Log.d("GPS-Provider-Enabled",s);
}
#Override
public void onProviderDisabled(String s) {
Intent activateGPSIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
activateGPSIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activateGPSIntent);
}
};
locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
//noinspection MissingPermission, listen for updates every 3 seconds
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000,0,listener);
Map<String, String> coordinates=getLastKnownLocation(locationManager);
broadcastLocation(coordinates.get("latitude"),coordinates.get("longitude"));
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ClearFromRecentService", "Service Started");
Log.d("LAST_LAT_AND_LONG",lastKnownLatitude+" "+lastKnownLongitude);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("ClearFromRecentService", "Service Destroyed, Removing update location listener");
//unregistering the listener
/*if(locationManager != null){
locationManager.removeUpdates(listener);
}*/
}
public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearFromRecentService", "END");
//here you can call a background network request to post you location to server when app is killed
Toast.makeText(getApplicationContext(), "I'm still getting user coordinates", Toast.LENGTH_LONG).show();
//stopSelf(); //call this method to stop the service
}
private void broadcastLocation(String latitude,String longitude){
Intent intentSendLocationMainActivity = new Intent("location_update");
intentSendLocationMainActivity.putExtra("latitude",latitude);
intentSendLocationMainActivity.putExtra("longitude",longitude);
//I need to differentiate here if the app is killed or not to send the location to main activity or to a server
sendBroadcast(intentSendLocationMainActivity);
}
private Map<String, String> getLastKnownLocation(LocationManager lm){
Location lastKnownLocation=lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
String ll=""+lastKnownLocation.getLatitude();
Map<String, String> coordinates = new HashMap<String, String>();
// Check everytime this value, it may be null
if(lastKnownLocation != null){
coordinates.put("latitude",""+lastKnownLocation.getLatitude());
coordinates.put("longitude",""+lastKnownLocation.getLongitude());
}else{
coordinates.put("latitude","0");
coordinates.put("longitude","0");
}
return coordinates;
}
}
#Override
public void onCreate() {
super.onCreate();
locationManager = (LocationManager) getApplicationContext()
.getSystemService(Context.LOCATION_SERVICE);
Location lastKnownLocation = locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
// Check everytime this value, it may be null
if(lastKnownLocation != null){
double latitude = lastKnownLocation.getLatitude();
double longitude = lastKnownLocation.getLongitude();
// Use values as you wish
}
broadcastLocation("0","0");
....
}
I am having a problem where if my phone's location services is turned off, I want to set an alert to open the location services settings and turn it on there. This works fine but the issue is when I navigate back to my application after turning it on. It seems like when I click back, the onResume of my activity is called but the location is still not set correctly there until onResume is completed. Here is my code:
Comments.java:
#Override
public void onResume() {
super.onResume();
locationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
Location location = LocationService.getLastLocation();
if(location == null) {
if(getLastKnownLocation() == null) {
if(checkLocationEnabled()); {
location = getLastKnownLocation();
}
}
}
if(location != null) {
progressOverlay = fragmentView.findViewById(R.id.progress_overlay);
AndroidUtils.animateView(progressOverlay, View.VISIBLE, 0.9f, 200);
//Within this function call, the progress overlay is set to View.GONE.
databaseQuery.getPublicPosts(location, progressOverlay, fragmentView, getContext());
} else {
//We need to handle an error saying that location is not enabled for this device and exit them out of the app
}
}
private Location getLastKnownLocation() {
locationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
List<String> providers = locationManager.getProviders(true);
Location bestLocation = null;
for (String provider : providers) {
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return null;
} else {
Location l = locationManager.getLastKnownLocation(provider);
if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
// Found best last known location: %s", l);
bestLocation = l;
}
}
}
return bestLocation;
}
public boolean checkLocationEnabled() {
try {
gps_enabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch(Exception ex) {
}
try {
network_enabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch(Exception ex) {
}
if(!gps_enabled && !network_enabled) {
// notify user
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
dialog.setMessage(getContext().getResources().getString(R.string.gpsNetworkNotEnabled));
dialog.setPositiveButton(getContext().getResources().getString(R.string.openLocationSettings), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
Intent myIntent = new Intent( Settings.ACTION_LOCATION_SOURCE_SETTINGS);
getContext().startActivity(myIntent);
//get gps
}
});
dialog.setNegativeButton(getContext().getString(R.string.cancel), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
}
});
dialog.show();
}
return gps_enabled || network_enabled;
}
What I do is when the phone location services is off, I set up an alert that takes you to the location services screen. When the user turns on the location services screen, if he or she clicks "back" on his or her phone, they should be taken back to the initial screen that showed that their location services was off alert. However, when navigating back to that screen, my progressOverlay variable is still visible because the progressOverlay is set to View.GONE when the databaseQuery.getPublicPosts call goes through. That doesn't seem to happen because the if(location != null) statement is probably false right when we navigate back to the screen, but is probably set to true after the phone waits a few seconds and fully obtains the phone's location. It's interesting because if I put a debugger anywhere in that code, the location services will be set correctly and my progressOverlay will go away because the location gets set while I am debugging the code. Is there any way I can "wait" or continue to show the loading screen until the location services is fully on before calling databaseQuery.getPublicPosts? Any help would be appreciated.
hi why trying to use getLastKnownLocation.It return your previous latitude and longitude and it might take some time to update lastknown location.
Most probably it retrun you null after on your location.
I'm just referring you google Fused api for location any kind of updation or current.Its very accurate.
How you can fused api in your project.See i'll give you small example.
Step 1. Make this class GoogleLocationService.java
public class GoogleLocationService {
private GoogleServicesCallbacks callbacks = new GoogleServicesCallbacks();
LocationUpdateListener locationUpdateListener;
Context activity;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 30000;
public GoogleLocationService(Context activity, LocationUpdateListener locationUpdateListener) {
this.locationUpdateListener = locationUpdateListener;
this.activity = activity;
buildGoogleApiClient();
}
protected synchronized void buildGoogleApiClient() {
//Log.i(TAG, "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(activity)
.addConnectionCallbacks(callbacks)
.addOnConnectionFailedListener(callbacks)
.addApi(LocationServices.API)
.build();
createLocationRequest();
mGoogleApiClient.connect();
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
private class GoogleServicesCallbacks implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
#Override
public void onConnected(Bundle bundle) {
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) {
Toast.makeText(activity, "Google play service not updated", Toast.LENGTH_LONG).show();
}
locationUpdateListener.cannotReceiveLocationUpdates();
}
#Override
public void onLocationChanged(Location location) {
if (location.hasAccuracy()) {
if (location.getAccuracy() < 30) {
locationUpdateListener.updateLocation(location);
}
}
}
}
private static boolean locationEnabled(Context context) {
boolean gps_enabled = false;
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
try {
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
ex.printStackTrace();
}
return gps_enabled;
}
private boolean servicesConnected(Context context) {
return isPackageInstalled(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, context);
}
private boolean isPackageInstalled(String packagename, Context context) {
PackageManager pm = context.getPackageManager();
try {
pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}
}
public void startUpdates() {
/*
* Connect the client. Don't re-start any requests here; instead, wait
* for onResume()
*/
if (servicesConnected(activity)) {
if (locationEnabled(activity)) {
locationUpdateListener.canReceiveLocationUpdates();
startLocationUpdates();
} else {
locationUpdateListener.cannotReceiveLocationUpdates();
Toast.makeText(activity, "Unable to get your location.Please turn on your device Gps", Toast.LENGTH_LONG).show();
}
} else {
locationUpdateListener.cannotReceiveLocationUpdates();
Toast.makeText(activity, "Google play service not available", Toast.LENGTH_LONG).show();
}
}
//stop location updates
public void stopUpdates() {
stopLocationUpdates();
}
//start location updates
private void startLocationUpdates() {
if (checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(activity, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, callbacks);
}
}
public void stopLocationUpdates() {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, callbacks);
}
}
public void startGoogleApi() {
mGoogleApiClient.connect();
}
public void closeGoogleApi() {
mGoogleApiClient.disconnect();
}
}
Step2. Make this interface
LocationUpdateListener.java
public interface LocationUpdateListener {
/**
* Called immediately the service starts if the service can obtain location
*/
void canReceiveLocationUpdates();
/**
* Called immediately the service tries to start if it cannot obtain location - eg the user has disabled wireless and
*/
void cannotReceiveLocationUpdates();
/**
* Called whenever the location has changed (at least non-trivially)
* #param location
*/
void updateLocation(Location location);
/**
* Called when GoogleLocationServices detects that the device has moved to a new location.
* #param localityName The name of the locality (somewhere below street but above area).
*/
void updateLocationName(String localityName, Location location);
}
Step 3. Use this piece of code where you want to get location
public class MainActivity extends ActionBarActivity {
private GoogleLocationService googleLocationService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() {
#Override
public void canReceiveLocationUpdates() {
}
#Override
public void cannotReceiveLocationUpdates() {
}
//update location to our servers for tracking purpose
#Override
public void updateLocation(Location location) {
if (location != null ) {
Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());
}
}
#Override
public void updateLocationName(String localityName, Location location) {
googleLocationService.stopLocationUpdates();
}
});
googleLocationService.startUpdates();
}
#Override
public void onDestroy() {
super.onDestroy();
if (googleLocationService != null) {
googleLocationService.stopLocationUpdates();
}
}
}
Updated answer
how to send and receive lat lng through broadcast
call this onlocationchanged
Intent sendBroadIntentPND = new Intent("COORD_BROADCAST_FILTER");
sendBroadIntentPND.putExtra("LOC_LAT", location.getLatitude());
sendBroadIntentPND.putExtra("LOC_LON", location.getLongitude());
getApplicationContext().sendBroadcast(sendBroadIntentPND);
then where you want to receive the location call this way
// declear global variable
private final String COORD_BROADCAST_FILTER = "COORD_BROADCAST_FILTER";
private Location newLocation;
private BroadcastReceiver coordinateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
try {
newLocation.setLatitude(intent.getDoubleExtra("LOC_LAT", 0.0));
newLocation.setLongitude(intent.getDoubleExtra("LOC_LON", 0.0));
} catch (Exception e) {
e.printStackTrace();
}
}
};
Register you receiver oncreate,
getActivity().registerReceiver(coordinateReceiver, new IntentFilter(COORD_BROADCAST_FILTER));
unregister your receiver ondestroy
getActivity().unregisterReceiver(coordinateReceiver);
Thanks hope this help you.
First you have an unwanted semi-colon in if(checkLocationEnabled()); which acts as an empty statement causing the second call to getLastKnownLocation() to always execute (this will most probably not affect the problem).
The LocationManager class has a constant KEY_STATUS_CHANGED that is the key of a bundle extra that is passed when a status change is broadcast. See the documentation related to the constant. To trigger this change you need to call [LocationManager.requestLocationUpdates](https://developer.android.com/reference/android/location/LocationManager.html#requestLocationUpdates(long, float, android.location.Criteria, android.app.PendingIntent)) so that the location manager receives such a status update.
From the documentation of this method, you can either use a BroadcastReceiver to receive the update by filtering on the above constant. Here is a related post that does this. Or you can use another overloaded version of the request updates method that takes a LocationListener which has an onStatusChanged callback.
A similar update exists when a location provider is enabled, with the KEY_PROVIDER_ENABLED. You can try listening for both updates if the status update didn't work.
Note that when requesting location updates for providers, in order to include updates for all providers including disabled ones, you should retrieve them using LocationManager.getProviders(false).
Once you receive the appropriate update events, you can call removeUpdates to remove the update requests.
Please take note that
The Google Play services location APIs are preferred over the Android
framework location APIs (android.location) as a way of adding location
awareness to your app. If you are currently using the Android
framework location APIs, you are strongly encouraged to switch to the
Google Play services location APIs as soon as possible.
I've tried calling this Service in two different ways, but it didn't seem to work.
The first way was:
startService(new Intent(getBaseContext(), LocationService.class));
for which I get an error saying `
Caused by: `java.lang.IllegalStateException: GoogleApiClient is not connected yet.
Then I tried this:
Intent serviceIntent = new Intent();
serviceIntent.setAction("com.parseapp.eseen.eseen.service.LocationService");
startService(serviceIntent);
Also it didn't work, in contrary absolutely nothing happens, nothing shows up in logcat. Can anyone help?
Here's the whole code:
LocationService.class
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener {
// LogCat tag
private static final String TAG = LocationService.class.getSimpleName();
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 1000;
private Location mLastLocation;
// Google client to interact with Google API
private GoogleApiClient mGoogleApiClient;
// boolean flag to toggle periodic location updates
private boolean mRequestingLocationUpdates = false;
private LocationRequest mLocationRequest;
// Location updates intervals in sec
private static int UPDATE_INTERVAL = 10000; // 10 sec
private static int FATEST_INTERVAL = 5000; // 5 sec
private static int DISPLACEMENT = 10; // 10 meters
#Override
public void onCreate() {
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
togglePeriodicLocationUpdates();
}
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FATEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
}
private void togglePeriodicLocationUpdates() {
mGoogleApiClient.connect();
if (!mRequestingLocationUpdates) {
mRequestingLocationUpdates = true;
startLocationUpdates();
Log.d(TAG, "Periodic location updates started!");
} else {
mRequestingLocationUpdates = false;
// Stopping the location updates
stopLocationUpdates();
Log.d(TAG, "Periodic location updates stopped!");
}
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
}
protected void startLocationUpdates() {
mGoogleApiClient.connect();
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
#Override
public void onConnected(Bundle arg0) {
createLocationRequest();
}
#Override
public void onConnectionSuspended(int arg0) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = "
+ result.getErrorCode());
}
#Override
public void onLocationChanged(Location location) {
// Assign the new location
mLastLocation = location;
Toast.makeText(getApplicationContext(), "Location changed!",
Toast.LENGTH_SHORT).show();
}
#Override
public boolean stopService(Intent name) {
return super.stopService(name);
}
SearchActivity.class
button = (Button)findViewById(R.id.buttonPressed);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent serviceIntent = new Intent();
serviceIntent.setAction("com.parseapp.eseen.eseen.service.LocationService");
startService(serviceIntent);
}
});
AndroidManifest.xml
<service android:name=".LocationService">
<intent-filter>
<action android:name=".LocationService"> </action>
</intent-filter>
</service>
Your first attempt to start the service using the class name worked:
startService(new Intent(getBaseContext(), LocationService.class));
The exception occurred after the service started executing. Your code cannot use LocationServices until the GoogleApi connection is successful, namely after onConnected() is called. Your code calls connect() multiple times before getting to startLocationUpdates(), but does not wait for onConnected() to be called. That method is the notification you receive when the connection has been made and LocationServices can be used.
This demo code is for an AsyncTask, not a Service, but gives an idea of how the connection processing can be done using a CountDownLatch.
you package name com.parseapp.eseen.eseen
and You are tryng to call using com.parseapp.eseen.eseen.service.LocationService as action but you mentioned in Manifest as .LocationService that means you should call using com.parseapp.eseen.eseen.LocationService
in startLocationUpdates(); you are calling connect again and requesting api before connecting
request for locationupdates from onConnected method
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
I'm a bit confused in deciding the way i should implement my background custom service class.
What I need :
location updates and activity recognition.
REST API call and the Geofence API.
I'm new to background processing in Android and I'm not sure how to implement this.
Is it possible to make a ThreadPoolExecutor (a task manager class, runnables, etc.) and deal with inter-thread communication ?
If yes,is it possible to listen to location updates inside a Thread class ?
for now, I'm using this library ( Smart-location-lib ) to listen to location updates.
is there a better solution ?
Any help is greatly appreciated !
This is the best way using Google API in background service
And you didn't explain what do you mean by activity recognition?
public class MyLocationService extends Service implements LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
public MyLocationService() {
}
#Override
public void onCreate() {
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API).
addConnectionCallbacks(this).
addOnConnectionFailedListener(this)
.build();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mGoogleApiClient.connect();
Toast.makeText(this, "Location services started", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onLocationChanged(Location location) {
System.out.println("MyLocationService.onLocationChanged");
// TODO with location updateshere
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(1000); // Update location every second
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
Location loc = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
sendMyLocUpdate(loc, UserProfile.DRIVER);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
#Override
public void onDestroy() {
Toast.makeText(this, "Location services stopped", Toast.LENGTH_LONG).show();
Location loc = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
sendMyLocUpdate(loc, UserProfile.PASSENGER);
mGoogleApiClient.disconnect();
super.onDestroy();
}
}