I'm developing an app which uses the location service from the start. My phone (Sony Z3 Compact) has a list of apps which used the location service in the location settings menu. It has each app with a "battery usage" message, like low battery usage, high battery usage etc. My app is listed as "High battery usage". Other apps which use location data like Tinder has it as "low battery usage".
I'd like to know what causes this and what's the best way to save battery. I see that each location provider has a "battery usage" data. Does that mean if I only use network provider as opposed to GPS provider I get the "low battery usage" stamp?
Or does it dependent on requesting location updates? Because I need the location only once, however I might want to check if the user has moved, for that I'd need to turn location updates on.
Any ideas regarding this?
Here is the service I'm using:
public class LocationTracker2 extends Service implements LocationListener {
protected Context context;
protected LocationManager locationManager;
protected OnLocationChanged event;
protected Location currentLocation;
public interface OnLocationChanged {
public void onLocationChanged(double latitude, double longitude);
}
public LocationTracker2(Context context, OnLocationChanged event) {
this.context = context;
this.event = event;
locationManager = (LocationManager)context.getSystemService(LOCATION_SERVICE);
}
public boolean canGetLocation() {
// getting GPS status
boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
// getting network status
boolean isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
return (isGPSEnabled || isNetworkEnabled);
}
public void showSettingsAlert(){
AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
// Setting Dialog Title
alertDialog.setTitle("GPS is settings");
// Setting Dialog Message
alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
// On pressing Settings button
alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
context.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
public Location getCurrentLocation() {
if(currentLocation != null) return currentLocation;
List<String> providers = locationManager.getProviders(true);
Location bestLocation = null;
for (String provider : providers) {
Location l = locationManager.getLastKnownLocation(provider);
if (l == null) {
continue;
}
if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
// Found best last known location: %s", l);
bestLocation = l;
}
}
if(bestLocation != null) {
currentLocation = bestLocation;
this.event.onLocationChanged(currentLocation.getLatitude(), currentLocation.getLongitude());
}
return currentLocation;
}
public void startLocationUpdates() {
Criteria criteria = new Criteria();
String bestProvider = locationManager.getBestProvider(criteria, false);
locationManager.requestLocationUpdates(bestProvider, 60000, 10, this);
}
public void stopLocationUpdates() {
locationManager.removeUpdates(this);
}
#Override
public void onLocationChanged(Location location) {
this.currentLocation = location;
this.event.onLocationChanged(this.currentLocation.getLatitude(), this.currentLocation.getLongitude());
}
#Override
public void onProviderDisabled(String provider) {}
#Override
public void onProviderEnabled(String provider) {}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
When your app is using the GPS provider, it will be listed as High Usage app. The network provider will be listed as low usage.If you will be using the GPS provider for only one point, you will be listed as High Usage, but for a short period.
I can suggest two alternatives for you:
First try to use the Passive Provider, if another app is using the location services at the moment you will receive their updates as well but the system will list them as the App who uses the battery.
Use the Google play location client, it will list the battery usage on Google Play Services instead of on you.
Related
I have a requirement of sending a user's location to a web server after a certain interval, currently sending after every two minutes. It is working fine unless the app is running. When the app is in background location stops getting updated, sending the same location again and again. I am little confused. it would be great if anyone suggests an alternative to my approach. I start location sending on click of a button and does not stop unless and until the user clicks on the button to stop. Below is my code. I want to send current location of the user to a web server whether the app is running or not. Any help is appreciated. problem is not in sending location after a certain interval , problem is location stops getting updated if I kill the app coordinates remain the same. If app is running in foreground then even a slight tilt in device makes the coordinates change. I earlier used firebaseJobDispatcher to call this service. Problem is not in calling the service. Problem is location stops getting updated and remains same every time I call the service if I have killed the app.
I am using alarm manager to call this service every 2 minutes.
public class GPSTracker_DUP extends Service implements LocationListener {
private Context mContext=null;
RetrofitAPI retrofitAPI;
// flag for GPS status
boolean isGPSEnabled = false;
// flag for network status
boolean isNetworkEnabled = false;
// flag for GPS status
boolean canGetLocation = false;
SaveData objSaveData;
Location location; // location
double latitude; // latitude
double longitude; // longitude
private String provider;
// The minimum distance to change Updates in meters
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
// The minimum time between updates in milliseconds
private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute
// Declaring a Location Manager
protected LocationManager locationManager;
public GPSTracker_DUP(Context context) {
this.mContext = context;
//getLocation();
}
public GPSTracker_DUP(){}
public Location getLocation() {
try {
locationManager = (LocationManager) mContext
.getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();
provider = locationManager.getBestProvider(criteria, false);
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, 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 null;
}
// getting GPS status
isGPSEnabled = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if(!isGPSEnabled)
{
showSettingsAlert();
}
else
{
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000*60*2,0,this);
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
Log.e("Provider ", provider + " has been selected."+location.getLatitude()+"==="+location.getLongitude());
saveLocation(location.getLatitude(),location.getLongitude());
//onLocationChanged(location);
}
}
// getting network status
// isNetworkEnabled = locationManager
// .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception e) {
e.printStackTrace();
}
return location;
}
public static boolean isConnected(Context context){
NetworkInfo info = getNetworkInfo(context);
return (info != null && info.isConnected());
}
public static NetworkInfo getNetworkInfo(Context context){
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo();
}
/**
* Stop using GPS listener
* Calling this function will stop using GPS in your app
* */
public void stopUsingGPS(){
if(locationManager != null){
locationManager.removeUpdates(GPSTracker_DUP.this);
}
}
/**
* Function to get latitude
* */
public double getLatitude(){
if(location != null){
latitude = location.getLatitude();
}
// return latitude
return latitude;
}
/**
* Function to get longitude
* */
public double getLongitude(){
if(location != null){
longitude = location.getLongitude();
}
// return longitude
return longitude;
}
/**
* Function to check GPS/wifi enabled
* #return boolean
* */
public boolean canGetLocation() {
return this.canGetLocation;
}
/**
* Function to show settings alert dialog
* On pressing Settings button will lauch Settings Options
* */
public void showSettingsAlert(){
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
// Setting Dialog Title
alertDialog.setTitle("GPS is settings");
// Setting Dialog Message
alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
// On pressing Settings button
alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
#Override
public void onLocationChanged(Location location) {
latitude = location.getLatitude();
longitude = location.getLongitude();
Log.e("onlocation","changed");
}
public void saveLocation(Double latitude,Double longitude){
objSaveData = new SaveData(mContext);
Log.e("Saving Coordinates", latitude + " " + longitude);
AudioDbHelper audioDbHelper= new AudioDbHelper(mContext);
UserCoordinates userCoordinates = new UserCoordinates();
userCoordinates.setLatitude(String.valueOf(latitude));
userCoordinates.setLongitude(String.valueOf(longitude));
userCoordinates.setUploaded("no");
SaveData objSaveData = new SaveData(mContext);
userCoordinates.setUserEmail(objSaveData.getString("LoginId"));
String time = new SimpleDateFormat("hh:mm: aa").format(Calendar.getInstance().getTime());
userCoordinates.setLocationTime(time);
audioDbHelper.addCoordinates(userCoordinates);
}
#Override
public void onDestroy() {
super.onDestroy();
stopUsingGPS();
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
Since You need to send location after some interval, It's better to use Job Scheduler which starts the service after an interval, fetches the location and makes the API call.
The best is Job Scheduler and is what is recommended by Google, but Android versions limit it's use, Its better to use Evernote Android Job. Depending on the Android version either the JobScheduler, GcmNetworkManager or AlarmManager is used.
You don't have to worry about the service getting killed as now it's the OS's responsibility to start the service
Also about fetching the location, Use Google PLay Location, to fetch location. What it does is it fetches the location from Google Play service in your device which is updated from time to time.
Take a look at this Util class that I use in one of my projects to fetch location from a running service. It is in Kotlin and uses Dagger2, but you will get the idea. It has a callback interface which replies with the current location and address fetched via Google Play Location Service
ServiceLocationUtil.kt
You need to create a Start_Sticky service for this by default its START_STICKY_COMPATIBILITY. Override onStartCommand().
#Override
public int onStartCommand(Intent intent, int flags, int startId){
// Do your Stuff
return START_STICKY;
}
Whereas due to Background Limitations this will not work in latest versions of android. So you probably want to checkout Background Location Limits and Android 8.0 Behavior Changes.
I suggest you to use JobShedular if you need to send location after some interval. There are some available like Evernote android-job , Firebase JobDispatcher.
Also read Intelligent Job-Scheduling.
You should use WorkManager or FirebaseJobDispatcher for background processes. But FirebaseJobDispatcher is not supported from Android Q.
This is my solution using WorkManager for getting location in background
Define this in activity or fragment
private fun startTaskWithWorkManager() {
val constraints: Constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val locationWorker =
PeriodicWorkRequest.Builder(LocationWorker::class.java, MIN_PERIODIC_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)
.setConstraints(constraints)
.addTag(LOCATION_WORKER_TAG)
.setInputData(createInputData())
.build()
WorkManager.getInstance()
.enqueueUniquePeriodicWork(LOCATION_WORKER_TAG, ExistingPeriodicWorkPolicy.KEEP, locationWorker)
}
After that you should create a class that will extends from ListenableWorker. In my case I should have to use ListenableWorker instead of Worker. The difference you can find here and here.
class LocationWorker(context: Context, private val workerParams: WorkerParameters) :
ListenableWorker(context, workerParams) {
lateinit var mFuture: SettableFuture<ListenableWorker.Result>
private var fusedLocationProviderClient = FusedLocationProviderClient(context)
#SuppressLint("RestrictedApi", "MissingPermission")
override fun startWork(): ListenableFuture<Result> {
val uniqueId = workerParams.inputData.getString(UNIQUE_ID_KEY)
mFuture = SettableFuture.create()
Timber.d("mFutureStart")
fusedLocationProviderClient.lastLocation.addOnSuccessListener { location ->
Timber.d("location == $location")
if (location != null) {
mFuture.set(Result.success())
} else mFuture.set(Result.failure())
}
return mFuture
}
}
Thats it :)
Work like a charm
In my code when layout loads it fetch gps coordinate .This is my sample code. It works fine if GPS is off. If i turn on the gps its not loading the gps coordinates . I need to get user gps coordinates when he turn on GPS. so what is the problem . where i need to change. Sorry for my English.
dialog = new ProgressDialog(FXPage.this);
dialog.show();
dialog.setMessage("Getting Coordinates");
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 100000000,
1, this);
} else if (locationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 100000000,
1, this);
}
else {
dialog.dismiss();
Toast.makeText(getApplicationContext(), "Enable Location", Toast.LENGTH_LONG).show();
}
protected void refresh() {
super.onResume();
this.recreate();
}
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
dialog.show();
latitude = location.getLatitude();
longitude =location.getLongitude();
if (latitude != 0 && longitude != 0){
edittext6.setText(location.getLatitude()+","+location.getLongitude());
dialog.dismiss();
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
GPS dosen't works under roof.
You can get Location updates from Network provider first if its available.
If N/A then only request it from GPS and start a CountDownTimer
Now after timer expires u can check if the location is still null then Alert user "unable to get location update" and stop the GPS location update request.
to Avoid all above trouble you may simply want to use FusedLocation Api which work better than the previous LocationApi.
check this link for FusedLocation api.
This part of your code seems to be really strange:
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
dialog.show();
latitude = location.getLatitude();
longitude =location.getLongitude();
if (latitude != 0 && longitude != 0){
edittext6.setText(location.getLatitude()+","+location.getLongitude());
dialog.dismiss();
}
}
Basically you are showing and then immediately dismissing a dialog. Maybe you should use a timer to dismiss the dialog after coordinates are shown.
Here are some good advices to get a single location update.
First of all check that the GPS is enabled, this will make your workflow more robust:
final LocationManager manager = (LocationManager) getSystemService( Context.LOCATION_SERVICE );
if ( !manager.isProviderEnabled( LocationManager.GPS_PROVIDER ) ) {
buildAlertMessageNoGps();
}
private void buildAlertMessageNoGps() {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Your GPS seems to be disabled, do you want to enable it?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(#SuppressWarnings("unused") final DialogInterface dialog, #SuppressWarnings("unused") final int id) {
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, #SuppressWarnings("unused") final int id) {
dialog.cancel();
}
});
final AlertDialog alert = builder.create();
alert.show();
}
Then before asking the LocationManager for new location updates I would check if there is a good enough "last known location" (this obviously depends on the precision you need).
For example you can iterate through each location provider to find the most timely and accurate last known location as shown here:
List<String> matchingProviders = locationManager.getAllProviders();
for (String provider: matchingProviders) {
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
float accuracy = location.getAccuracy();
long time = location.getTime();
if ((time > minTime && accuracy < bestAccuracy)) {
bestResult = location;
bestAccuracy = accuracy;
bestTime = time;
}
else if (time < minTime &&
bestAccuracy == Float.MAX_VALUE && time > bestTime){
bestResult = location;
bestTime = time;
}
}
}
If the last known location is not recent enough you may request a single location update using the fastest location provider available:
if (locationListener != null &&
(bestTime < maxTime || bestAccuracy > maxDistance)) {
IntentFilter locIntentFilter = new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION);
context.registerReceiver(singleUpdateReceiver, locIntentFilter);
locationManager.requestSingleUpdate(criteria, singleUpatePI);
}
Obviously you need to configure a BroadcastReceiver:
protected BroadcastReceiver singleUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
context.unregisterReceiver(singleUpdateReceiver);
String key = LocationManager.KEY_LOCATION_CHANGED;
Location location = (Location)intent.getExtras().get(key);
if (locationListener != null && location != null)
locationListener.onLocationChanged(location);
locationManager.removeUpdates(singleUpatePI);
}
};
in my application i have a button that start counting the time how long the user has been in the same location. and i want the app stop the timer counting if the user goes out from the area he started so i made a class that give me the current location (LAT,LONG,PLACE NAME) and i don't know how to use the "onLocationChanged" and Should I use it? or something else for what i need?
public class MapCurrentPlace extends Service implements LocationListener {
private final Context mContext;
public static final String MY_TEMP = "sharedFile";
SharedPreferences setting;
SharedPreferences.Editor editor;
// flag for GPS status
boolean isGPSEnabled = false;
// flag for network status
boolean isNetworkEnabled = false;
// flag for GPS status
boolean canGetLocation = false;
Location location; // location
double latitude; // latitude
double longitude; // longitude
String placeName = "";
// The minimum distance to change Updates in meters
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 0; // 10 meters
// The minimum time between updates in milliseconds
private static final long MIN_TIME_BW_UPDATES = 500; // 1 minute
// Declaring a Location Manager
protected LocationManager locationManager;
public MapCurrentPlace(Context context) {
this.mContext = context;
getLocation();
}
public String getPlaceName(){
Geocoder gc = new Geocoder(mContext);
try {
List<Address> list = gc.getFromLocation(latitude, longitude, 1);
if(list.size()>0){
String city = list.get(0).getLocality();
String street = list.get(0).getAddressLine(0);
placeName = city+", "+street+"";
}
} catch (IOException e) {
placeName = "";
e.printStackTrace();
}
return placeName;
}
public Location getLocation() {
try {
locationManager = (LocationManager) mContext
.getSystemService(Context.LOCATION_SERVICE);
// getting network status
isNetworkEnabled = locationManager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
// getting GPS status
isGPSEnabled = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!isGPSEnabled) {
// no network provider is enabled
this.canGetLocation = false;
return null;
} else {
this.canGetLocation = true;
if (isNetworkEnabled) {
locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER,MIN_TIME_BW_UPDATES,0, this);
if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
// if GPS Enabled get lat/long using GPS Services
if (isGPSEnabled) {
if (location == null) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,MIN_TIME_BW_UPDATES,0, this);
if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return location;
}
/**
* Stop using GPS listener
* Calling this function will stop using GPS in your app
* */
public void stopUsingGPS(){
if(locationManager != null){
locationManager.removeUpdates(MapCurrentPlace.this);
}
}
/**
* Function to get latitude
* */
public double getLatitude(){
if(location != null){
latitude = location.getLatitude();
}
// return latitude
return latitude;
}
/**
* Function to get longitude
* */
public double getLongitude(){
if(location != null){
longitude = location.getLongitude();
}
// return longitude
return longitude;
}
/**
* Function to check GPS/wifi enabled
* #return boolean
* */
public boolean canGetLocation() {
return this.canGetLocation;
}
public boolean getIsNetworkEnabled() {
return this.isNetworkEnabled;
}
/**
* Function to show settings alert dialog
* On pressing Settings button will lauch Settings Options
* */
public void showSettingsAlert(){
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
// Setting Dialog Title
alertDialog.setTitle("GPS OFF");
// Setting Dialog Message
alertDialog.setMessage("Allow GPS");
// On pressing Settings button
alertDialog.setPositiveButton("SETTING", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
locationManager.getLastKnownLocation
this one returns last know location fix. This might be kind of old if no apps requested location for a while.
Location updates will be fired each time device location was changed.
So it's up to you what to use. If you want to get up-to date location, use location updates. But bear in mind that location updated drain battery.
Also you might want to look into Geofences: http://developer.android.com/training/location/geofencing.html
The onLocationChanged method will be called when the current location is changed. This is a default method called because you have implemented the LocationListener.
I do not know how you are implementing the timer (perhaps an AsyncTask so your app can do other stuff as well - http://developer.android.com/reference/android/os/AsyncTask.html), but then in the OnLocationChanged method add some call to your timer variable to stop.
OnLocationChanged will trigger whenever the device travels the minDistance parameter of requestLocationUpdates, from the last relevant location of your device (i.e. since the event last triggered).
Of course, the trigger rate also takes into account the minTime parameter, which should be used to prevent saturation of the calls, and thus saving battery/data usage (e.g. car travelling too fast for your minDistance).
As you have it now (you supplied an inline 0 to the call), you need to manually check if the most recent OnLocationChanged data is a relevant change to your scenario.
It's much more transparent to just use that parameter as the system tries to make it hardware-independent and battery-efficient. You already have a MIN_DISTANCE_CHANGE_FOR_UPDATES in your boilerplate code, just add it as a parameter instead of '0' as you have, and set it to the number of meters you need:
// The minimum distance to change Updates in meters
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 30; //30 meters
//...
//at some point while your service/activity is running
locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDATES,MIN_DISTANCE_CHANGE_FOR_UPDATES,
this);
//...
#Override
public void onLocationChanged(Location currentLocation) {
//triggered when System asserts a 30 meter variation from last relevant location
doMyStuff();
}
Side note: remember most GPS hardware has issues with very fine locations, especially at sub-10m.
You can also keep the value at 0, receive updates at the granularity the hardware updates itself, and do the checks manually - just use the Location parameter supplied with the event and do your own checks to decide, which is pretty much a geo-fencing check:
Location lastRelevantLocation;
#Override
public void onLocationChanged(Location currentLocation) {
// called when the listener is notified with a location update from the GPS
// hardware update rate might call this more than your CPU should handle
if(checkOutsidePerimeter(location)){
lastRelevantLocation = currentLocation;
doMyStuff();
}
}
Remember minTime if you want to keep tabs on granularity.
I am developing an android application. I need to find the location of the user as soon as he/she logs in to the application. I do not want maps to be displayed, the location should be identified without the user's knowledge. Is it possible to do this using the Google maps API? or is there any other way to do this?
Thanks
The best way to do this is to use the PASSIVE location provider like so:
LocationManager lm = (LocationManager)yourActivityContext.getSystemService(Context.LOCATION_SERVICE);
Location lastKnown = lm.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
This returns the last known location received by the operating system, so this may be stale, but you can check when the location was retrieved, and by which provider by querying the location object.
In conclusion, the user will have no idea that you've gotten a location except that your app will require the proper location permission(s).
Try this,
protected LocationManager locationManager;
Context context;
public String gps_loc;
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 10, new MyLocationListener());
private class MyLocationListener implements LocationListener {
public void onLocationChanged(Location location) {
gps_loc = String.format("%1$s" +"-"+"%2$s",location.getLongitude(), location.getLatitude());
Toast.makeText(Clockin.this, gps_loc, Toast.LENGTH_SHORT).show();
}
public void onStatusChanged(String s, int i, Bundle b) {
Toast.makeText(class.this, "Provider status changed", Toast.LENGTH_SHORT).show();
}
public void onProviderDisabled(String s) {
Toast.makeText(class.this,"Provider disabled by the user. GPS turned off",Toast.LENGTH_SHORT).show();
final AlertDialog alertDialog = new AlertDialog.Builder(class.this).create();
alertDialog.setTitle("Activate GPS...");
alertDialog.setButton("ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
startActivityForResult(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS), 0);
}
});
alertDialog.show();
}
public void onProviderEnabled(String s) {
Toast.makeText(class.this,"Provider enabled by the user. GPS turned on", Toast.LENGTH_SHORT).show();
}
}
I have some Android code that needs to get the best available location QUICKLY, from GPS, network or whatever is available. Accuracy is less important than speed.
Getting the best available location is surely a really standard task. Yet I can't find any code to demonstrate it. The Android location code expects you to specify criteria, register for updates, and wait - which is fine if you have detailed criteria and don't mind waiting around.
But my app needs to work a bit more like the Maps app does when it first locates you - work from any available provider, and just check the location isn't wildly out of date or null.
I've attempted to roll my own code to do this, but am having problems. (It's inside an IntentService where an upload happens, if that makes any difference. I've included all the code for info.) What's wrong with this code?
#Override
protected void onHandleIntent(Intent arg0) {
testProviders();
doUpload();
}
private boolean doUpload() {
int j = 0;
// check if we have accurate location data yet - wait up to 30 seconds
while (j < 30) {
if ((latString == "") || (lonString == "")) {
Log.d(LOG_TAG, "latlng null");
Thread.sleep(1000);
j++;
} else {
Log.d(LOG_TAG, "found lat " + latString + " and lon " + lonString);
break;
}
//do the upload here anyway, with or without location data
//[code removed for brevity]
}
public boolean testProviders() {
Log.e(LOG_TAG, "testProviders");
String location_context = Context.LOCATION_SERVICE;
locationmanager = (LocationManager) getSystemService(location_context);
List<String> providers = locationmanager.getProviders(true);
for (String provider : providers) {
Log.e(LOG_TAG, "registering provider " + provider);
listener = new LocationListener() {
public void onLocationChanged(Location location) {
// keep checking the location - until we have
// what we need
//if (!checkLoc(location)) {
Log.e(LOG_TAG, "onLocationChanged");
locationDetermined = checkLoc(location);
//}
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status,
Bundle extras) {
}
};
locationmanager.requestLocationUpdates(provider, 0,
0, listener);
}
Log.e(LOG_TAG, "getting updates");
return true;
}
private boolean checkLoc(Location location) {
float tempAccuracy = location.getAccuracy();
int locAccuracy = (int) tempAccuracy;
Log.d(LOG_TAG, "locAccuracy = " + locAccuracy);
if ((locAccuracy != 0) && (locAccuracy < LOCATION_ACCURACY)) {
latitude = location.getLatitude();
longitude = location.getLongitude();
latString = latitude.toString();
lonString = longitude.toString();
return true;
}
return false;
}
public void removeListeners() {
// Log.e(LOG_TAG, "removeListeners");
if ((locationmanager != null) && (listener != null)) {
locationmanager.removeUpdates(listener);
}
locationmanager = null;
// Log.d(LOG_TAG, "Removed " + listener.toString());
}
#Override
public void onDestroy() {
super.onDestroy();
removeListeners();
}
Unfortunately, this finds the network provider, but only ever outputs latlng null 30 times - it never seems to get a location at all. I never even get a log statement of locationChanged.
It's funny, because from ddms I can see output like:
NetworkLocationProvider: onCellLocationChanged [305,8580]
NetworkLocationProvider: getNetworkLocation(): returning cache location with accuracy 75.0
seeming to suggest that the network provider does have some location info after all, I'm just not getting at it.
Can anyone help? I think working example code would be a useful resource for the Android/StackOverflow community.
You are definitely trying to do this the hard way. Here are some snippets from a new app I am working on. It uses Criteria to get all providers capable of returning a fine level of accuracy without a cost.
If no providers are enabled a dialog is displayed that prompts the user to turn on their location settings. If the user hits ok an Intent is actually fired that sends them to the settings on their phone. If there are providers enabled the app takes the most recent last known location from any of the enabled providers. For my app I just need to know what general area the user is in and it's likely that the last known location is from their home area.
If providers are enabled the loop also requests location updates as quickly as possible. This is ideal for my app but you can change this to conserve battery my modifying the arguments to the requestLocationUpdates method.
The optimization that this code has that the examples on the Android app don't really show is that all of the enabled providers are started simultaneously. All of the providers will return separate updates on to the onLocationChanged method. In my app I remove the location listener after one of the providers returns a location with a good enough accuracy.
Start Location Updates:
void getCurrentLocation() {
List<String> providers = locationManager.getProviders(criteria, true);
if (providers != null) {
Location newestLocation = null;
for (String provider : providers) {
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
if (newestLocation == null) {
newestLocation = location;
} else {
if (location.getTime() > newestLocation.getTime()) {
newestLocation = location;
}
}
locationManager.requestLocationUpdates(provider, 0, 0, this);
}
}
} else {
LocationDialogFragment dialog = new LocationDialogFragment();
dialog.show(getSupportFragmentManager(),
LocationDialogFragment.class.getName());
}
}
Receive Location Update:
#Override
public void onLocationChanged(Location location) {
float bestAccuracy = -1f;
if (location.getAccuracy() != 0.0f
&& (location.getAccuracy() < bestAccuracy) || bestAccuracy == -1f) {
if (location.getAccuracy() < Const.MIN_ACCURACY) {
locationManager.removeUpdates(this);
}
}
bestAccuracy = location.getAccuracy();
}
Location Settings Dialog:
public class LocationDialogFragment extends DialogFragment {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.location_dialog_message)
.setPositiveButton(R.string.location_dialog_positive_button,
new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent settingsIntent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(settingsIntent);
}
})
.setNegativeButton(R.string.location_dialog_negative_button,
new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getActivity(),
R.string.no_location_message, Toast.LENGTH_LONG)
.show();
}
});
return builder.create();
}
}
Thread.sleep() in production code is a serious code smell IMHO. If you find you're having to do that, you're probably doing something that's not supposed to work that way. In this case, I think it's the source of your problem -- you're not letting Android go back to process this thread's message queue to dispatch any location updates it finds. I suspect an IntentService is just not going to work for your scenario.