Location-tracking app suspends in background after few minutes - ios

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.

Related

iOS Location Services Enabled/Disabled event when application closed

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.

Xamarin Estimote not finding beacons for iOS

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.

Update interval value for continuous GPS location

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.

GPS in window phone (movement from current position when i am move on the way. how?)

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;
}
}

BlackBerry - Simulate a KeyPress event

I have a BlackBerry application that needs to take pictures from the camera and send them to a server. In order to do this i invoke the native camera application and listen to the filesystem. Once an image is captured and saved as a new jpeg file i get notified, resume foreground control and go about my business. The problem starts occurring after the first time this cycle is completed because now when i decide to call the camera application again it is already opened, and now the user is seeing a thumbnail of the last picture that was taken and several buttons allowing him to manipulate/manage it. naturally what i want the user to see is a preview of what the camera is "seeing" before he snaps another photo as he did before.
I have thought of various ways to solve this including killing the camera app each time (I understand this cannot be done programatically?), sending CameraArguments when invoking the app (which appears to be useless), and now i was thinking a solution could be as simple generating a "Back" key event before switching back to my app which would theoretically dismiss the annoying edit screen. Could this really be done? and if not is there any other possible solution you may think of?
A kind of hack...
start Camera App
in TimerTask check if Camera App started and if it need to be closed (some flag)
if yes, invoke it(so it will became active) and push ESC keypress event injection to close it
Take a look at this:
class Scr extends MainScreen {
boolean killCameraApp = false;
final String mCameraModuleName = "net_rim_bb_camera";
final CameraArguments args = new CameraArguments();
public Scr() {
super();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if (isCameraRunning() && killCameraApp) {
getApplication().invokeAndWait(callCamera);
getApplication().invokeAndWait(killCamera);
}
}
}, 0, 100);
}
Runnable callCamera = new Runnable() {
public void run() {
callCamera();
}
};
Runnable killCamera = new Runnable() {
public void run() {
injectKey(Characters.ESCAPE);
killCameraApp = false;
}
};
private boolean isCameraRunning() {
boolean result = false;
ApplicationManager appMan =
ApplicationManager.getApplicationManager();
ApplicationDescriptor[] appDes = appMan.getVisibleApplications();
for (int i = 0; i < appDes.length; i++) {
result = mCameraModuleName.equalsIgnoreCase(appDes[i]
.getModuleName());
if (result)
break;
}
return result;
}
private void callCamera() {
Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA,
new CameraArguments());
}
private void injectKey(char key) {
KeyEvent inject = new KeyEvent(KeyEvent.KEY_DOWN, key, 0);
inject.post();
}
protected void makeMenu(Menu menu, int instance) {
menu.add(new MenuItem("start camera", 0, 0) {
public void run() {
callCamera();
killCameraApp = false;
}
});
menu.add(new MenuItem("kill app", 0, 0) {
public void run() {
killCameraApp = true;
}
});
super.makeMenu(menu, instance);
}
}
EDIT: Don't forget to set permissions for device release:
Options => Advanced Options => Applications => [Your Application] =>Edit Default permissions =>Interactions =>key stroke Injection

Resources