I am using GPS either GPS_PROVIDER or NETWORK_SERVICE_PROVIDER for latitude and longitude . I want to detect when specifically App goes background to foreground (Either By Home button) .
ISSUES:
GPS detected location is not updated from time to time until any app used it so can get the updated location .
Detect the Background to foreground of app and detect location without battery issues .
Any GPS location update time to time without any battery issues.
For get location in background frequently use FusedLocation provider in a android service class.
Fused location provide give more accurate location and consume less power.
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener,LocationListener{
private static final String TAG = "LocationService";
private static final int LOCATION_INTERVAL = 1000;
private Context mContext;
private LocationRequest locationRequest;
private GoogleApiClient googleApiClient;
private FusedLocationProviderApi fusedLocationProviderApi;
#Override
public IBinder onBind(Intent arg0){
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
Log.e(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onCreate(){
Log.e(TAG, "onCreate");
mContext = this;
getLocation();
}
#Override
public void onDestroy(){
Log.e(TAG, "onDestroy");
super.onDestroy();
try{
if(googleApiClient!=null){
googleApiClient.disconnect();
}
}
catch(Exception e){
e.printStackTrace();
}
}
private void getLocation(){
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(LOCATION_INTERVAL);
locationRequest.setFastestInterval(LOCATION_INTERVAL);
fusedLocationProviderApi = LocationServices.FusedLocationApi;
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
if (googleApiClient != null) {
googleApiClient.connect();
}
}
#Override
public void onConnected(Bundle arg0) {
// Location location = fusedLocationProviderApi.getLastLocation(googleApiClient);
fusedLocationProviderApi.requestLocationUpdates(googleApiClient, locationRequest, this);
}
#Override
public void onConnectionSuspended(int arg0) {
}
#Override
public void onResponse(int reqCode, int statusCode, String json) {
}
#Override
public void onCancel(boolean canceled) {
}
#Override
public void onProgressChange(int progress) {
}
#Override
public void onLocationChanged(Location location) {
Toast.makeText(mContext, "Driver location :"+location.getLatitude()+" , "+location.getLongitude(), Toast.LENGTH_SHORT).show();
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
}
}
and call service from your activity-
startService(new Intent(mContext,LocationService.class));
hope it will help you.
EDITED
Also add below code to your manifest -
<service
android:name=".LocationService">
</service>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Related
I want to implement location tracking in android but in a small time interval (seconds), after searching and reading the documentation i found that there is restrictions on location access when app is in the background, and the solution is to use foreground service, But i need another legal and efficient solution without the need to show notification to the user.
You could use something like that
public class GPSService extends Service {
private LocationListener listener;
private LocationManager locationManager;
public IBinder onBind(Intent intent){
return null;
}
#SuppressLint("MissingPermission")
#Override
public void onCreate() {
super.onCreate();
listener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Intent i = new Intent("location_update");
i.putExtra("coordinates",location);
sendBroadcast(i);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
};
locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000,0,listener);
}
#Override
public void onDestroy() {
super.onDestroy();
if (locationManager != null){
locationManager.removeUpdates(listener);
}
}
}
And retreive by broadcaster
I have implemented Google Location Services inside a service, so that I can get location updates in the background (so that updates don't stop when user locks screen, switches fragment, etc.). But I am getting a strange error message that is stopping me from implementing it.
On the line:
"LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);"
It is telling me that 2rd parameter 'this' is not allowed and should be cast to (com.google.android.gms.location.LocationListener). But in every example I've seen this is not the case. When I do cast it, it does not work
Below is my code, trouble is in the onConnected method
public class LocationTrackingService extends Service implements LocationListener, ConnectionCallbacks, OnConnectionFailedListener {
GoogleApiClient googleApiClient = null;
LocationRequest locationRequest;
Location currentLocation;
static final int INTERVAL = 2000;
static final int FASTEST_INTERVAL = 500;
static final int PRIORITY = LocationRequest.PRIORITY_HIGH_ACCURACY;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
setupGoogleApiClient();
createLocationRequest(INTERVAL, FASTEST_INTERVAL, PRIORITY);
googleApiClient.connect();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!googleApiClient.isConnected()) {
googleApiClient.connect();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
//TODO maybe dont need this
googleApiClient.disconnect();
super.onDestroy();
}
#Override
public void onLocationChanged(Location location) {
//updating current location
currentLocation = location;
//TODO will be writing directly to database here
//broadcasting intent
Intent publishLocationUpdate = new Intent("location_update");
publishLocationUpdate.putExtra("current_location", currentLocation);
sendBroadcast(publishLocationUpdate);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
//if location tracking is not enabled
#Override
public void onProviderDisabled(String s) {
Intent toSettings = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
toSettings.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(toSettings);
}
#Override
public void onConnected(#Nullable Bundle bundle) {
//sets location to last known location
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.
return;
}
currentLocation = FusedLocationApi.getLastLocation(googleApiClient);
//request updates
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
// Create an instance of GoogleAPIClient which we call to recieve location information
void setupGoogleApiClient(){
if (googleApiClient == null) {
googleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
//set request settings, interval: how soon until new request is made, priority affects batterylife (using GPS, netowork, data...)
void createLocationRequest(int interval, int fastestInterval, int priority) {
locationRequest = new LocationRequest();
locationRequest.setInterval(interval);
locationRequest.setFastestInterval(fastestInterval);
locationRequest.setPriority(priority);
//can also use PRIORITY_LOW_POWER, PRIORITY_BALANACED_POWER_ACCURACY, PRIORITY_NO_POWER
}
}
I check the intent in onResume in my fragment, code below:
if(broadcastReceiver == null){
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Location currentLocation;
currentLocation = (Location) intent.getExtras().get("current_location");
currentAltitudeTextView.setText(String.valueOf(currentLocation.getLatitude()));
currentSpeedTextView.setText(String.valueOf(currentLocation.getLongitude()));
}
};
}
context.registerReceiver(broadcastReceiver, new IntentFilter("location_update"));
I'm registering my app after boot to receive location updates. My boot receiver is starting a service that does the initialization:
#Override
protected void onHandleIntent(Intent intent) {
GoogleApiClient client = _googleApiBuilder.get()
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
client.connect();
}
Sometimes in the onConnected callback method I'm getting exception indication I'm not connected yet. After some research I've encountered this - GoogleApiClient is throwing "GoogleApiClient is not connected yet" AFTER onConnected function getting called
It made me think, does the way I'm initializing google api is correct ? e.g. should I initialize it in a service ?
What is the suggested way to do it in the background ?
Hope it will be helpful to you.
public class LocationService extends Service implements ConnectionCallbacks,
OnConnectionFailedListener, LocationListener {
private static final String TAG = LocationService.class.getSimpleName();
private GoogleApiClient mGoogleApiClient;
#Override
public IBinder onBind(final Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
initGoogleApi();
}
#Override
public int onStartCommand(final Intent intent, final int flags,
final int startId) {
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public void onLocationChanged(final Location location) {
}
#Override
public void onConnectionFailed(final ConnectionResult result) {
}
#Override
public void onConnected(final Bundle bundale) {
createLocationRequest();
}
#Override
public void onConnectionSuspended(final int arg0) {
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
}
protected void createLocationRequest() {
final LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(Constants.INTERVAL);
mLocationRequest.setFastestInterval(Constants.FAST_INTERVAL);
mLocationRequest
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setSmallestDisplacement(0);
startLocationUpdates(mLocationRequest);
}
private void initGoogleApi() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API).addApi(ActivityRecognition.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
}
}
Used the following solution instead:
googleApiInstance.blockingConnect(10, TimeUnit.SECONDS);
or in other words instead of running the operation in a different thread (that's what connect is doing), I'm running it in my own thread (I'm using IntentService) and manage the connection life cycle on my own.
I am trying to make a location tracking application on an android phone, it will be just same as the built-in google map in the phone. It will continue to track the phone even the app is close, so it will be running in the background of other applications.
I am thinking of splitting up both the classes, User Interface (Google Map) and location tracking, where the location tracking will always run but the google map will only display when the user open the application. Thus this is what I reached so far.
MapDisplay class for Google Map
public class MapDisplay extends Activity {
private GoogleMap map;
private LocationService locationService;
private Intent locationServiceIntent;
private Location myLocation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map_display);
map = ((MapFragment) getFragmentManager().findFragmentById(R.id.googleMap)).getMap();
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
map.setMyLocationEnabled(true);
locationServiceIntent = new Intent(this, LocationService.class);
x=0;
}
#Override
protected void onPause() {
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
startService(locationServiceIntent);
bindService(locationServiceIntent, locationServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
public void updateLocation()
{
// for testing purpose, to make sure it will update periodically
Toast.makeText(MapDisplay.this, String.valueOf(myLocation.getLatitude()), Toast.LENGTH_SHORT).show();
}
private ServiceConnection locationServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
locationService = ((LocationService.LocationBinder)service).getService();
myLocation = locationService.getMyLocation();
updateLocation();
}
#Override
public void onServiceDisconnected(ComponentName name) {
locationService = null;
}
};
}
LocationService class
public class LocationService extends Service implements LocationListener
{
private LocationManager locationManager;
private Location myLocation;
private IBinder locationBinder = new LocationBinder();
public class LocationBinder extends Binder
{
public LocationService getService()
{
return LocationService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return locationBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
myLocation = null;
boolean networkProviderAvailable = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
boolean gpsProviderAvailable = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if(networkProviderAvailable)
{
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
}
else if(gpsProviderAvailable)
{
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}
else
{
stopSelf();
}
return super.onStartCommand(intent, flags, startId);
}
public Location getMyLocation()
{
return myLocation;
}
#Override
public void onLocationChanged(Location location) {
myLocation = location;
}
#Override
public void onProviderDisabled(String arg0) {
}
#Override
public void onProviderEnabled(String arg0) {
}
#Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
}
}
Using code above I experienced application crash because of this line
myLocation = locationService.getMyLocation();
String.valueOf(myLocation.getLatitude())
It can't get the location from LocationService class.
So, my questions are:
1) is this a good way to make the application?
2) how to periodically call the updateLocation() in MapDisplay when there are new updates in onLocationChanged method?
I will be much appreciate if someone can help me on this.
You have to account for the case that you have not yet received a location. To do that, your client code needs to check the return of getLocation for null, and handle accordingly.
Additionally you may want some more intelligence in onLocationChanged. Right now if a gps location comes in and then a network location comes in, the network location will overwrite the gps location, despite being less accurate. I'd take a look at some code I wrote at http://gabesechansoftware.com/location-tracking/ You'll see a class FallbackLocationTracker which will track both gps and network location, returning the most accurate location received so far. I think it does what you're trying to do.
i'm currently writing an app that should to the following:
The UI only contains one toggle button. If it is turned on the GPS Position shall be sent to an external server. If it is turned off, nothing shall happen.
If the Button was turned on and the app (activity) is closed the location should still be send until the Button is turned off again.
How can i achieve this ? I read a lot of threads and tutorials and on dev.google.com, but i was not able to find the best solution for my problem.
My current approach:
MainActivity.java
public void onClick(View v) {
if (onOffButton.isChecked()) {
Intent startIntent = new Intent(this, LocationService.class);
startService(startIntent);
} else {
stopService(new Intent(this, LocationService.class));
}
}
LocationService.java
public class LocationService extends Service implements LocationListener {
final public static String START_ACTION = "START_LOCATION";
final public static int NOTE_ID = 1;
private int updateRate;
private LocationManager locationManager;
private NotificationManager notifyManager;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
// show popup message
Toast.makeText(this, getText(R.string.start_message), Toast.LENGTH_SHORT).show();
// display icon in status bar
requestLocationUpdates();
}
private void requestLocationUpdates() {
if(locationManager != null)
locationManager.removeUpdates(this);
// get location service
Criteria crit = new Criteria();
crit.setPowerRequirement(Criteria.ACCURACY_FINE);
String bestProvider = getLocationManager().getBestProvider(crit, true);
getLocationManager().requestLocationUpdates(bestProvider, updateRate * 1000,
0 /* minDist */, this);
LocationService.running = true;
}
#Override
public void onDestroy() {
super.onDestroy();
getLocationManager().removeUpdates(this);
notifyManager.cancel(NOTE_ID);
Toast.makeText(this, getText(R.string.stop_message), Toast.LENGTH_SHORT).show();
LocationService.running = false;
}
#Override
public void onLocationChanged(Location location) {
//Send Location to Server
}
#Override
public void onProviderDisabled(String provider) {
// TODO stop service, notify user
}
#Override
public void onProviderEnabled(String provider) {
requestLocationUpdates();
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO notify user
}
private LocationManager getLocationManager() {
if (this.locationManager == null)
this.locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
return this.locationManager;
}
}
I got this from a very old gps tracker i found on the Internet (i know that onStart() is deprecated and should replaced with onCommandStart()) I just want to know if the general approach is good..
Regards.
The approach looks fine. You just need to implement the call-back methods to report your location.