I'm currently using CLLocationManager to always track geofences even when the application is in the background. I can't seem to find a way to listen for when location services is enabled/disabled.
Is it possible to listen for a location service enable/disable event or when location is enabled/disabled for your specific application while the application is closed?
Please note I'm using Xamarin, but Objective-C code is fine.
public class LocationManager
{
protected CLLocationManager locationManager;
public LocationManger()
{
this.locationManager = new CLLocationManger();
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
locationManager.RequestAlwaysAuthorization();
}
// ... get array of CLCircularRegion and start listening to each
// locationManager events...
locationManager.RegionEntered += (sender, e) => { /*stuff*/ };
locationManager.RegionLeft += (sender, e) => { /*stuff*/ };
locationManager.DidDetermineState += (sender, e) => { /*stuff*/ };
//locationaManager.SomeSortOfLocationServiceEnableDisableEvent += (sender, e) => { /*stuff*/ };
}
}
A call to the class method [CLLocationManager locationServicesEnabled] returns a BOOL indicating whether location services are enabled or not.
If the user disables location services, locationManager:didChangeAuthorizationStatus: will be called on a CLLocationManagerDelegate.
Therefore, if you have a class conform to CLLocationManagerDelegate and implement locationManager:didChangeAuthorizationStatus:, you should be able to handle the disable event by the user.
Related
I am trying to get the location. I implement everything but nothing works.
This is my class:
public class Best2 : CLLocationManagerDelegate
{
public CLLocationManager _locationManager;
private Int32 _numEvento;
public Best2()
{
}
protected Best2(NSObjectFlag t) : base(t)
{
}
protected internal Best2(IntPtr handle) : base(handle)
{
}
public Best2(Int32 paramEvento)
{
_numEvento = paramEvento;
_locationManager = new CLLocationManager ();
_locationManager.PausesLocationUpdatesAutomatically = false;
_locationManager.DesiredAccuracy = CLLocation.AccurracyBestForNavigation;
if (UIDevice.CurrentDevice.CheckSystemVersion (9, 0)) {
_locationManager.AllowsBackgroundLocationUpdates = true;
}
_locationManager.Delegate = this;
StartLocationUpdates();
}
public void StartLocationUpdates ()
{
if (CLLocationManager.LocationServicesEnabled) {
_locationManager.RequestLocation();
System.Diagnostics.Debug.WriteLine(
String.Format(
"FA&)#2 RequestLocation() Data:D {0}"
, DateTime.UtcNow
)
);
}
}
public override void Failed (CLLocationManager manager, Foundation.NSError error)
{
System.Diagnostics.Debug.WriteLine(
String.Format(
"FA&)#2 Failed() Data:D {0}"
, DateTime.UtcNow
)
);
}
public override void UpdatedLocation (CLLocationManager manager, CLLocation newLocation, CLLocation oldLocation)
{
System.Diagnostics.Debug.WriteLine(
String.Format(
"FA&)#2 UpdatedLocation() Data:D {0}"
, DateTime.UtcNow
)
);
}
I call this way:
Best2 position = new Best2(1);
Based in the apple documentation, I think it should call the method Failed if not works and UpdatedLocation if works. But nothing is calling.
I already try to put the class receiving events, but it doesn't work either.
According to the documentation about Request Location we know that if we want to use location in iOS we must follow the steps below:
Add the NSLocationWhenInUseUsageDescription key and the NSLocationAlwaysAndWhenInUseUsageDescription key to your Info.plist file.
If your app supports iOS 10 and earlier, add the NSLocationAlwaysUsageDescription key to your Info.plist file.
Then before RequestLocation() we need to call RequestAlwaysAuthorization() or RequestWhenInUseAuthorization() to get user's authentication.
At last we can get the location but you misunderstand the event:
I think it should call the method Failed if not works and
UpdatedLocation if works.
If it works the event LocationsUpdated() will fire not UpdatedLocation().
I try to create a location-tracking app. App should work in background. So, I switch on properties "Enable background modes", "location updates" and added parameter "NSLocationAlwaysUsageDescription" to the source.
On ios7 app works fine, but on ios8 it suspends in background after few minutes (How app should work: I send a request every time when new location is received, and if I can see this request on the server it means that app is working).
I downloaded xamarin.mobile component with location functionality and used it instead of my class for geolocation. App also suspends in the background.
I created Objective-C app with the same functionality and tested it on the same device. Result - app works fine (as expected).
So, maybe app still needs some setting or I'm missing something?
public class LocationUpdatedEventArgs : EventArgs
{
CLLocation location;
public LocationUpdatedEventArgs(CLLocation location)
{
this.location = location;
}
public CLLocation Location
{
get { return location; }
}
}
protected CLLocationManager locMgr;
public event EventHandler<LocationUpdatedEventArgs> LocationUpdated = delegate { };
public GeoLocationService_iOS()
{
this.locMgr = new CLLocationManager();
LocationUpdated += SaveLocation;
locMgr.AuthorizationChanged += (object sender, CLAuthorizationChangedEventArgs e) =>
{
//CheckStatus();
};
if (locMgr.RespondsToSelector(new Selector("requestAlwaysAuthorization")))
{
locMgr.RequestAlwaysAuthorization();
}
locMgr.DistanceFilter = 1;
locMgr.DesiredAccuracy = CLLocation.AccuracyBest;
locMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) =>
{
// fire our custom Location Updated event
this.LocationUpdated(this, new LocationUpdatedEventArgs(e.Locations[e.Locations.Length - 1]));
};
locMgr.StartUpdatingLocation();
}
public void SaveLocation(object sender, LocationUpdatedEventArgs e)
{
SendLoc();
}
Adding
locMgr.PausesLocationUpdatesAutomatically = false;
solved my problem.
Im just trying to get a really simple POC of my multiplatform app finding ibeacons in a crossplatform xamarin solution. I've got the android side of things going but just hitting issues with the iOS side of things.
I've got the following hacked up code in the AppDelegate class (this is just to first muck around with it, i realize this isnt where it should reside):
[Register("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
static readonly string uuid = "B9407F30-F5F8-466E-AFF9-25556B57FE6D";
static readonly string monkeyId = "Monkey";
CBPeripheralManager peripheralMgr;
BTPeripheralDelegate peripheralDelegate;
CLLocationManager locationMgr;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
myAppManagerApp.Init(typeof(myAppManagerApp).Assembly);
Forms.Init();
// FormsMaps.Init();
UINavigationBar.Appearance.BackgroundColor = UIColor.FromRGBA(0, 0, 0, 0);
UINavigationBar.Appearance.TintColor = UIColor.Blue;
UINavigationBar.Appearance.SetTitleTextAttributes(new UITextAttributes()
{
TextColor = UIColor.White
});
window.RootViewController = BuildView();
window.MakeKeyAndVisible();
var monkeyUUID = new NSUuid(uuid);
var beaconRegion = new CLBeaconRegion(monkeyUUID, monkeyId);
//power - the received signal strength indicator (RSSI) value (measured in decibels) of the beacon from one meter away
var power = new NSNumber(-59);
NSMutableDictionary peripheralData = beaconRegion.GetPeripheralData(power);
peripheralDelegate = new BTPeripheralDelegate();
peripheralMgr = new CBPeripheralManager(peripheralDelegate, DispatchQueue.DefaultGlobalQueue);
peripheralMgr.StartAdvertising(peripheralData);
locationMgr = new CLLocationManager();
locationMgr.RegionEntered += (object sender, CLRegionEventArgs e) =>
{
if (e.Region.Identifier == monkeyId)
{
var notification = new UILocalNotification() { AlertBody = "There's a monkey hiding nearby!" };
UIApplication.SharedApplication.PresentLocationNotificationNow(notification);
}
};
locationMgr.DidStartMonitoringForRegion += locationMgr_DidStartMonitoringForRegion;
locationMgr.MonitoringFailed += locationMgr_MonitoringFailed;
locationMgr.StartMonitoring(beaconRegion);
locationMgr.StartRangingBeacons(beaconRegion);
locationMgr.DidRangeBeacons +=locationMgr_DidRangeBeacons;
return true;
}
private void locationMgr_DidRangeBeacons(object sender, CLRegionBeaconsRangedEventArgs e)
{
throw new NotImplementedException();
}
private void locationMgr_MonitoringFailed(object sender, CLRegionErrorEventArgs e)
{
throw new NotImplementedException();
}
private void locationMgr_DidStartMonitoringForRegion(object sender, CLRegionEventArgs e)
{
int i = 0;
//throw new NotImplementedException();
}
static UIViewController BuildView()
{
var root = new pgeRoot();
var controller = root.CreateViewController();
return controller;
}
Ive chopped most of the code out of the Find the monkey sample. Either way the DidRangeBeacons or RegionEntered events never fire. I'm using estimote iBeacons so i dont know whether that makes a difference?
Any ideas on what im missing here? Is there a permission or setting i need to put into the plist?
Thanks
In iOS 8 you need to explicitly ask for permission to use Location Services - it's the CLLocationManager's RequestAlwaysAuthorization (for monitoring) and RequestWhenInUseAuthorization (for ranging) methods. You also need an appropriate (NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription) entry in the Info.plist file of your iOS app, though I'm not entirely surely how to do this in Xamarin.
I am working on a BlackBerry Application that is supposed to update the location at fixed intervals. The interval value can be selected/changed from a slider. It varies between 1 minute, 2 minutes, 5 minutes, 30 minutes etc. On the very first load (Start App), location interval is 30 seconds. After this, I store the slider value in a persistent store and location is updated accordingly with the set interval. Background thread running to update location is as follows:
private boolean startLocationUpdate()
{
boolean retval = false;
try
{
LocationProvider locationProvider = LocationProvider.getInstance(null);
if ( locationProvider == null )
{
Runnable showGpsUnsupportedDialog = new Runnable()
{
public void run()
{
Dialog.alert("GPS is not supported on this platform, exiting...");
//System.exit( 1 );
}
};
UiApplication.getUiApplication().invokeAndWait( showGpsUnsupportedDialog ); // Ask event-dispatcher thread to display dialog ASAP.
}
else
{
locationProvider.setLocationListener(new LocationListenerImpl(), interval, -1, -1);
retval = true;
}
}
catch (LocationException le)
{
System.err.println("Failed to instantiate the LocationProvider object, exiting...");
System.err.println(le);
System.exit(0);
}
return retval;
}
private class LocationListenerImpl implements LocationListener
{
public void locationUpdated(LocationProvider provider, Location location)
{
if(location.isValid())
{
double longitude = location.getQualifiedCoordinates().getLongitude();
double latitude = location.getQualifiedCoordinates().getLatitude();
updateLocationScreen(latitude, longitude);
}
}
public void providerStateChanged(LocationProvider provider, int newState)
{
}
}
private void updateLocationScreen(final double latitude, final double longitude)
{
UiApplication.getUiApplication().invokeAndWait(new Runnable()
{
public void run()
{
double lat = latitude;
double longi = longitude;
lblLatitude.setText(Double.toString(lat));
spacing.setText(", ");
lblLongitude.setText(Double.toString(longi));
}
});
}
Along with this, there is a "Refresh" button available that will start acquiring a location update immediately once clicked. This button calls a method is another class to acquire the location. The method is as follows:
try {
Criteria myCriteria = new Criteria();
myCriteria.setCostAllowed(false);
LocationProvider myLocationProvider = LocationProvider.getInstance(myCriteria);
double heading = 0;
double velocity = 0;
try {
Location myLocation = myLocationProvider.getLocation(6000);
if(myLocation.isValid())
{
double longitude = myLocation.getQualifiedCoordinates().getLongitude();
double latitude = myLocation.getQualifiedCoordinates().getLatitude();
}
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
//Dialog.alert("Location Updated");
}
});
setLocation(myLocation.getQualifiedCoordinates(),velocity,heading);
} catch ( InterruptedException iex ) {
System.out.println(iex.getMessage());
} catch ( LocationException lex ) {
System.out.println(lex.getMessage());
}
} catch ( LocationException lex ) {
System.out.println(lex.getMessage());
}
Problems I am facing:
1) Interval value not changing. I am implementing the change by picking the value from the persistent store as:
if (PersistentStoreHelper.persistentHashtable.containsKey("gpsInterval"))
{
String intervalValue=((String) PersistentStoreHelper.persistentHashtable.get("gpsInterval"));
MyScreen.interval=Integer.parseInt(intervalValue);
}
This is never empty as navigation to this page inserts a value of 30 minutes to it.
2) Once the "Refresh" button is clicked, the background thread seems to be cancelled. It no longer runs at any interval value.
I read that there is only one instance of the location provider created and with "Refresh" it is cancelled after acquiring the location and thus the background thread stops. Is this true? If yes, how can I achieve my desired result.
EDIT: The gpsInterval value is read as follows:
if (PersistentStoreHelper.persistentHashtable.containsKey("gpsInterval"))
{
String intervalValue=((String)PersistentStoreHelper.persistentHashtable.get("gpsInterval"));
interval=Integer.parseInt(intervalValue);
}
else
{
interval=10;
}
Saving the Interval
So, first of all, make sure that when you let the user change the update interval, via the slider, you properly save it to the PersistentStore. The code should look something like this:
// NOTE: I would recommend persisting the slider value as an Integer, not a String,
// but, the original code used String, so that's what this uses
hashtable.put("gpsInterval", (new Integer(intervalSlider.getValue())).toString());
PersistentObject po = PersistentStore.getPersistentObject(APP_BUNDLE_ID);
po.setContents(hashtable);
po.commit();
Since you didn't post that code, I just wanted to be sure that it was being saved to the persistent store correctly.
Updating the Location Provider / Listener
The other issue, that is a problem, is that you kick off the location updates in startLocationUpdate() with this code:
locationProvider.setLocationListener(new LocationListenerImpl(), interval, -1, -1);
That uses the value of the interval variable at the instant that setLocationListener() is called. If you later update the interval variable,
String intervalValue=((String) PersistentStoreHelper.persistentHashtable.get("gpsInterval"));
MyScreen.interval=Integer.parseInt(intervalValue);
this will have no effect on the location listener. It will keep updating with the original interval value, not the new one. You would have to call setLocationListener() again, with the new value of interval. With your code, you should probably just call startLocationUpdate() again:
String intervalValue=((String) PersistentStoreHelper.persistentHashtable.get("gpsInterval"));
MyScreen.interval=Integer.parseInt(intervalValue);
startLocationUpdate();
Refresh Problem
I'm not 100% sure, but my guess would be that in your existing code that's used when the Refresh button is pressed, you are changing to a different LocationProvider with different criteria. That's probably why the first one is cancelled.
Try changing your startLocationUpdate() method to save the provider as a member variable:
/** this is the one location provider used by this class! */
private LocationProvider _locationProvider;
private boolean startLocationUpdate()
{
boolean retval = false;
try
{
_locationProvider = LocationProvider.getInstance(null);
then, in your refresh code, use the same location provider to get the current location:
double heading = 0;
double velocity = 0;
try {
Location myLocation = _locationProvider.getLocation(6000);
if(myLocation.isValid())
Note: if you really do want to setCostAllowed(false), that's fine. Do that the first time that you assign the _locationProvider member variable. And use that provider/criteria both for normal periodic location updates, and your Refresh button handler. I think the key is to use the same provider, not create a new one with different criteria.
i am working with GPS in windows phone 7.1, i got the current position on bing map and get the nearest Restaurants showing on bing map. but now i want that when am moving on the way then my pushpin where show me is also move with me.
i.e. current position also want to change with my movement.
I hope this to help you. In a nutshell, you have to use object GeoCoordinateWatcher.
private void startLocationButton_Click(object sender, RoutedEventArgs e)
{
if (watcher == null)
{
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
watcher.MovementThreshold = 20;
watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
}
watcher.Start();
}
void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled:
MessageBox.Show("Location Service is not enabled on the device");
break;
case GeoPositionStatus.NoData:
MessageBox.Show(" The Location Service is working, but it cannot get location data.");
break;
}
}
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (e.Position.Location.IsUnknown)
{
MessageBox.Show("Please wait while your prosition is determined....");
return;
}
}