I've been experimenting with the Android SDK for a few weeks now, trying to achieve an accurate location from a background service.
After trying a few configurations, I currently have this on a loop:
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(true);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_HIGH);
bestProvider = locationManager.getBestProvider(criteria, true);
lastKnownLocation = locationManager.getLastKnownLocation(bestProvider);
And then I check lastKnownLocation every now and then for a position update. I know you can listen for updates but at the moment I'm not too concerned about that right now. What I am concerned about is, (I think) I'm asking for the phone to use GPS whenever possible - instead of other methods of determining the location - an yet it still returns a latitude / longitude from a good distance away, yet when I open the maps application, it has me within a couple of meters.
Can anyone suggest where I'm going wrong here?
Setting the Criteria just establishes which provider is best to use depending on them so that doesn't really have a say on the accuracy or the validity of the location. I just set the provider to GPS straight away (If GPS is available!).
Also it doesn't seem like your giving it any requirements concerning how long you want to wait before updating based on time and distance. Here is an example of what I do using intents and broadcast receiver. It may help you.
public void beginMonitoringLocation(int minDistance) {
IntentFilter filter = new IntentFilter();
filter.addAction(MainActivity.LOCATION_UPDATE_ACTION);
this.mContext.registerReceiver(this.locationReceiver, filter);
LocationManager mLocationManager = (LocationManager) this.mContext.getSystemService(Context.LOCATION_SERVICE);
mLocationManager.addGpsStatusListener(this);
boolean enabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!enabled) {
Log.e("LocationManager", "GPS not enabled!!!!");
}
LocationProvider provider = mLocationManager.getProvider(LocationManager.GPS_PROVIDER); // GET THE BEST PROVIDER FOR OUR LOCATION
Log.d("LocationManager:","Location Provider:"+provider);
if ( provider == null ) {
Log.e( "LocationManager", "No location provider found!" );
return;
}
final int locationUpdateRC=0;
int flags = 0;
Intent intent = new Intent(MainActivity.LOCATION_UPDATE_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.mContext, locationUpdateRC, intent, flags);
// PENDING INTENT TO BE FIRED WHEN THE LOCATIONMANAGER RECEIVES LOCATION UPDATE.
// THIS PENDING INTENT IS CAUGHT BY OUR BROADCAST RECEIVER
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,minDistance,pendingIntent);
this._monitoringLocation = true;
}
And then in the same class I put my broadcast receiver
public BroadcastReceiver locationReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Location location = (Location) intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED);
if (location != null) {
//Do something with it
}
}
};
The action for my intent filter is just a static reference to a constant set in my activity.
public static final String LOCATION_UPDATE_ACTION = "com.corecoders.sqlmaptrack.LOCATION_UPDATE_RECEIVED";
This worked in my case in providing me with accurate locations. You can set the distance to 0 if you want then what you will find is you get location fixes of an accuracy of 5 every second if you have a good fix of 4 satellites or more.
I hope this helps you
I have used the below code to get accurate location. Using below code you can handle enabling/disabling the GPS programmatically.
private void enableGPSTracking() {
new Thread() {
#Override
public void run() {
super.run();
try {
toggleGPS(true, getApplicationContext());
provider = LocationManager.GPS_PROVIDER;
Criteria locationCritera = new Criteria();
locationCritera.setAccuracy(Criteria.ACCURACY_FINE);
locationCritera.setAltitudeRequired(false);
locationCritera.setBearingRequired(false);
locationCritera.setCostAllowed(true); locationCritera.setPowerRequirement(Criteria.NO_REQUIREMENT);
provider = locationManager.getBestProvider(locationCritera,
true);
Intent intent = new Intent(
"com.example.gps.LOCATION_READY");
pendingIntent = PendingIntent.getBroadcast(
getApplicationContext(), 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
// Register for broadcast intents
locationManager.requestLocationUpdates(provider, 0, 0,
pendingIntent);
} catch (Exception e) {
}
}
}.start();
}
public static void toggleGPS(boolean flag, Context context) {
try {
Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", flag);
context.sendBroadcast(intent);
} catch (Exception e) {
}
}
to disable the GPS, simply pass false for flag value of ToggleGPS() method. Hope this helps you.
Related
my appwidget gets location to get forecast.
as title, cant get location at just after system rebooted.
LocationManager lm = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setBearingRequired(false);
criteria.setSpeedRequired(false);
criteria.setAltitudeRequired(false);
String provider = lm.getBestProvider(criteria, true);
Location loc = lm.getLastKnownLocation(provider);
double lat = loc.getLatitude(); //null
double lon = loc.getLongitude();/null
why these are null at just after system rebooted?
Using Broadcastreceiver on reboot
// Do something onReceive()
#Override
public void onReceive(Context context, Intent intent) {
//Do something after reboot
}
See this link:
Android BroadcastReceiver on startup - keep running when Activity is in Background
It is because on Boot up both GPS and Wifi/Data Connection is OFF.So,location is null.
You need to change a bit of code which can prompt the user to open GPS Settings or Wifi Settings.
I have searched quite a bit and I'm not totally clueless. I have implemented a temporary solution on my end but was wondering if there is a better approach out there.
I have an app that sends a person's location after every 60 seconds to a server. On my dashboard (the main screen that will go to onPause after application starts), I have registered a LocationManager with the following code:
service = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean enabled = service
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!enabled)
{
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
else
{
Criteria criteria = new Criteria();
provider = service.getBestProvider(criteria, false);
service.requestLocationUpdates(provider, 10000, 50, this);
Location location = service.getLastKnownLocation(provider);
// Initialize the location fields
if (location != null)
{
onLocationChanged(location);
}
else
{
Log.d("Location: ", "No update received");
}
}
However, as I mentioned, this activity will be minimized by the user (by pressing the home button). There is a service that gets called every 60 seconds by an AlarmManager. That service accesses static variables from the Dashboard Activity (lat, lon) and sends it to the server.
My question:
If the activity goes onPause, will the requestLocationUpdates function stop? Or will it keep working?
If it keeps working, it will keep updating the two lat and lon static String objects and the service will keep getting updated values. If they stop, the service will keep getting the same old values again and again.
Also, is there a better way to approach this problem? Using a mix of GPS Provider and Network Provider? (I need fairly accurate values).
EDIT
Here's my Alarm. This code is inside Login Activity
Intent i = new Intent(con, LocationPoller.class);
i.putExtra(LocationPoller.EXTRA_INTENT, new Intent(con,
Login.class));
i.putExtra(LocationPoller.EXTRA_PROVIDER,
LocationManager.GPS_PROVIDER);
gps = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getBroadcast(con, 0, i, 0);
gps.setRepeating(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(),
10 * 1000, pi);
Log.d("Service: ",
"GPS Service started and scheduled with AlarmManager");
Here's my receiver (also within Login activity)
private class ReceiveMessages extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Location loc = (Location) intent.getExtras().get(
LocationPoller.EXTRA_LOCATION);
String msg;
if (loc == null)
{
msg = intent.getStringExtra(LocationPoller.EXTRA_ERROR);
}
else
{
msg = loc.toString();
}
if (msg == null)
{
msg = "Invalid broadcast received!";
}
Log.d("GPS Broadcast: ", msg);
}
}
Nothing's happening :s Not getting anything on logcat which means the broadcast isn't being received.
When activity goes on pause, all registered listeners will stop. Better way to implement this is, alarm manager sent a broadcast every 60 seconds, this broadcast receiver starts a service and this service will request a location on Wakeful thread, once location information is retrieved, update the location on server.
There is an Open source library available with an example (courtesy CommonsWare), please refer below link. Its under Apache 2.0 license
Location Polling Library
Please find my sample project using above library. I have modified few things in the above library and created my own version.
Location Polling Demo Application
I am creating an Android application that stores a couple of conditions like Location, Time, Contact and Battery level. What I intend for my application to do is to store these conditions along with some phone setting changes that the user wants to activate if these conditions are met. It would be better if I put my questions in a list :
How do I check the Location of the device at regular intervals ? Is there a listener service (like for the Contacts option the TelephonyManager can be used) that can be used to listen for a callback in case the device location changed ?
Is there a way to schedule a particular function to be called based on Time in Android ?
Is there a way to call a particular function when the battery level of the device changes or the device is charging or unplugged ?
Here's your answer point wise:
1) You can use LocationManager to get the location of your device.
LocationManager locationManager = (LocationManager)
this.getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new LocationListener()
{
public void onLocationChanged(Location location)
{
makeUseOfNewLocation(location); //here you get the new location
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
You can also use GeoCoder class to exactly point out the address using the longitude and latitude received from locationManager.
Detail tutorial here - LocationManager
2) You can schedule the particular function to be called - using BroadcastReceiver and AlarmManager service.
private void setRecurringAlarm(Context context) {
// we know mobiletuts updates at right around 1130 GMT.
// let's grab new stuff at around 11:45 GMT, inexactly
Calendar updateTime = Calendar.getInstance();
updateTime.setTimeZone(TimeZone.getTimeZone("GMT"));
updateTime.set(Calendar.HOUR_OF_DAY, 11);
updateTime.set(Calendar.MINUTE, 45);
Intent downloader = new Intent(context, AlarmReceiver.class);
PendingIntent recurringDownload = PendingIntent.getBroadcast(context,
0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getActivity().getSystemService(
Context.ALARM_SERVICE);
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP,
updateTime.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, recurringDownload);
}
Detail tutorial BroadCastReceiver and AlarmManager
So, if you want you can recurrently check for the present location of device ( using LocationManager as shown in point 1) inside OnReceive of your BroadCastReceiver.
3) There is a BroadCastReceiver to catch when BatteryLevel changes as below:
private BroadcastReceiver mBatInfoReceiver
= new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent intent) {
int level = intent.getIntExtra("level", 0);
// do something...
}
}
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
For plugin or charging use following code:
// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// How are we charging?
int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
I Hope All this will help you...
I'm new to Android and I'm having the following problem. I'm writing a sample application where I have an intent service that first checks all location providers to get the last known location. If none of the last known locations provides an accurate (or timely) enough location then the location manager's requestLocationUpdates method is called with a BroadcastReceiver intent. Each time the broadcast receiver's onReceive method is called it should check the location to see if it is accurate and/or timely enough. I also have a TimerTask in the intent service that eventually goes off and should check to see if an accurate and/or timely enough location update has been obtained. The problem I'm having is that I don't know how to get the location data obtained in the broadcast receiver back to the intent service. Seems like this should be an easy thing to do but I've been agonizing over this for days. The only way I can think to do it is to write the data to an SQLite db in the broadcast receiver and then read those records back in the intent service, but this seems unnecessarily complicated. Does anyone know what the right way is to get the data back to the intent service? Should I even be using a broadcast receiver for requestLocationUpdates? Is there an easier way to do this? Here is the code
public class GetLocationService extends IntentService {
public GetLocationService() {
super("something");
}
LocationManager locationManager;
long maxFixLateness;
float maxFixPosUncertainty;
boolean usableLocObtained;
Location bestLoc = null;
float bestLocScore = 0;
Intent locChangeI;
PendingIntent pLocChangeI;
#Override
final protected void onHandleIntent(Intent intent) {
maxFixLateness = 30000;
maxFixPosUncertainty = 30;
long curTime = System.currentTimeMillis();
LocationManager locationManager = (LocationManager) this
.getSystemService(Context.LOCATION_SERVICE);
// Check for a usable location fix
List<string> matchingProviders = locationManager.getAllProviders();
for (String provider : matchingProviders) {
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
// ...some code to check if the location is accurate or timely
// enough
}
}
if (bestLoc == null) {
locChangeI = new Intent(this, HandleLocationUpdateReceiver.class);
pLocChangeI = PendingIntent.getBroadcast(this, 0, locChangeI,
PendingIntent.FLAG_UPDATE_CURRENT);
usableLocObtained = false;
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, pLocChangeI);
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, pLocChangeI);
// Call the timer that will periodically check to see if a usable
// location has been obtained.
new LocFixCheckTimer(60000, 30, 1000);
}
}
private class LocFixCheckTimer {
Timer timer;
long numChecks;
public LocFixCheckTimer(long initSearchTime, long maxRechecks,
long recheckFreq) {
numChecks = maxRechecks;
timer = new Timer();
// Wait 2 seconds before checking for a fix again
timer.schedule(new CheckLocTask(), initSearchTime, recheckFreq);
}
class CheckLocTask extends TimerTask {
public void run() {
if (numChecks > 0) {
if (usableLocObtained == true) {
// I want to use the location data obtained from the
// HandleLocationUpdateReceiver's onReceive method
// but I don't how to get that data here.
}
} else {
// Cancel the timer. We've timed-out on searching
// for a usable location fix
timer.cancel();
}
--numChecks;
}
}
}
}
Here is the broadcast receiver:
public class HandleLocationUpdateReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Location loc = (Location) intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED);
if (loc != null)
{
double lat = loc.getLatitude();
double lon = loc.getLongitude();
// Do some checking to see how accurate and timely the location is
// here and somehow get it back to the intent service.
}
}
}
Thanks for the help!
Use a listener for sending back data to your activity or service. It is provided in this link
i ve been facing some problems trying to pass data through intents and pending intents to a BroadcastReceiver, concerning proximity alerts. More specifically, am trying to pass an object, that among others holds the user's constantly changing position. I ve tried various tactics being proposed here (and not only) but none worked, resulting to either null values or same-as-first-time created intents, when the intent is retrieved on the BroadcastReceiver's side. Tactics used:
Flagging the intent that carries the object with:FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_CLEAR_TOP+FLAG_ACTIVITY_SINGLE_TOP
Result:Null values on the BroadacastReceiver's side
Flagging the pending intent created using the initial intent, with:FLAG_UPDATE_CURRENT or FLAG_CANCEL_CURRENT
Result:Null values on the BroadacastReceiver's side
Acquiring a random ID for intent or the pending intent using System.currentTimeMillis();
Result:Intents are not fired or received at all
Nothing described above. Result:Retrieving the same initial value every time.
Code for the calling method (stripped from any experimentations/producing null values):
private void setProximityAlert(MyCar myCar) {
String locService = Context.LOCATION_SERVICE;
LocationManager locationManager;
locationManager = (LocationManager)getSystemService(locService);
float radius = myCar.getMyCarRadius();
long expiration = myCar.getMyCarExpiration();
myService.setMyDriverLat(userLat);//setting user's position
myService.setMyDriverLng(userLng);//setting user's position
Intent intent = new Intent(myCar.getMyCarName());
intent.putExtra("myCar",myCar);
PendingIntent proximityIntent = PendingIntent.getBroadcast(this, -1, intent, 0);
locationManager.addProximityAlert(myCar.getMyCarLat(), myCar.getMyCarLng(), radius, expiration, proximityIntent);
}
Code for the calling method that sets the intent filter and registers the BroadcastReceiver:
public void addNewCarPoint (MyCar myCar){
IntentFilter filter = new IntentFilter(myCar.getMyCarName());
registerReceiver(new ProximityAlertReceiver(), filter);
setProximityAlert(myCar);
}
Code for the BroadcastReceiver's side:
public class ProximityAlertReceiver extends BroadcastReceiver {
#Override
public void onReceive (Context context, Intent intent) {
MyCar myCar=(MyCar)intent.getParcelableExtra("myCar");
driverLoc=(String)Double.toString(myCar.getMyDriverLat());
Toast.makeText(context, userLoc, Toast.LENGTH_SHORT).show();
Intent i = new Intent(context, MyCarDiscoveryPrompt.class);
context.startActivity(i);//firing intent
}
public void intentDataLoader(){
}
}
Any ideas would be more than welcome.
Thank you in advance.
Hmm i think i ve found something:
I placed the BroadcastReceiver (ProximityAlerReceiver), used to detect proximity alerts in the same class (MyCarTracking.class), where the LocationListener.class is located. This,
provides immediate access to fresh location updates, creating a new intent wrapped in a new pendingIntent to be fired to the BroadcastReceiver (only when the proximity criteria are met).
flags:FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_SINGLE_TOP and FLAG_CANCEL_CURRENT on intent and pendingIntent, were kept respectively. More specifically:
Code for LocationListener:
private final LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
updateWithNewLocation(location);//update application based on new location
}
public void onProviderDisabled(String provider){
updateWithNewLocation(null);//update application if provider disabled
}
public void onProviderEnabled(String provider){
// Update application if provider enabled
}
public void onStatusChanged(String provider, int status, Bundle extras){
//update application if provider hardware status changed
}
};
Code for setProximityAlert() method:
private void setProximityAlert() {
String locService = Context.LOCATION_SERVICE;
Context context =getApplicationContext();
LocationManager locationManager;
locationManager = (LocationManager)getSystemService(locService);
float radius = myCar.getMyCarRadius();
long expiration = myCar.getMyCarExpiration();
Intent intent = new Intent(CAR_DISCOVERED);
intent.putExtra("myCar",myCar);
locationManager.getLastKnownLocation(provider);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);//flagging intent
PendingIntent proximityIntent = PendingIntent.getBroadcast(context, -1, intent, PendingIntent.FLAG_CANCEL_CURRENT);//flagging pendingIntent
locationManager.addProximityAlert(myCar.getMyCarLat(), myCar.getMyCarLng(), radius, expiration, proximityIntent);//setting proximity alert
}
This solution works producing fresh intents with fresh location updates.
Thank you all for your help and your interest :)
Try adding
intent.setData(uri);
where uri is some unique value for each pending intent
I've been struggling with this problem as well. It took me a whole night to find that a weird bug I had was related to this issue.
Here's a good discussion on google code on the subject: http://groups.google.com/group/android-developers/browse_thread/thread/b2060b27c8934921
I've solved all my problems by (ab)using both the uri in SetData and the (reserved) request code in PendingEvent.GetWhatever.
I'm also using FLAG_CANCEL_CURRENT on my intents and making sure only pendingintents that share the same purpose get the same data, action and uri.
Hope it helps a little bit.