Monotouch : pause application unit dialog response - ios

How can i pause application for prevention from running next method unit client does not selected dialog buttons?
For example i am showing location update dialog for accessing location service and i want to pause my application for dialog response
public CLLocation UpdateUserLocation()
{
CLLocation currentLocation = null;
CLLocationManager LocMgr = new CLLocationManager();
if (CLLocationManager.LocationServicesEnabled)
{
if (UIDevice.CurrentDevice.CheckSystemVersion (6, 0))
{
LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) =>
{
currentLocation = e.Locations [e.Locations.Length - 1];
};
}
else
{
LocMgr.UpdatedLocation += (object sender, CLLocationUpdatedEventArgs e) =>
{
currentLocation = e.NewLocation;
};
}
LocMgr.StartUpdatingLocation ();
LocMgr.Failed += (object sender, NSErrorEventArgs e) =>
{
Console.WriteLine (e.Error);
};
}
else
{
currentLocation = null;
Console.WriteLine ("Location services not enabled, please enable this in your Settings");
}
if (currentLocation != null)
{
LocationDetector.Instance.UpdateCurrentArea (new MyLatLng (currentLocation.Coordinate.Latitude, currentLocation.Coordinate.Longitude));
}
return currentLocation;
}

If I am understanding your question correctly.
When you display a dialog box, you are wanting to stop execution of the current method from further executing until the user selects a dialog box response.
Once they have selected a response, you would then like to continue execution of the code in the same function, effectively achieving your 'pause' that you are after.
To achieve this in iOS you can use a TaskCompletionSource.
In the example below it shows a dialog box first, asking the user if they want some coffee and then waits for the user to respond.
Once the user responds, it then continues execution, within the same function, and displays a further message box that is dependent on the selection that the user made.
UIButton objButton1 = new UIButton (UIButtonType.RoundedRect);
objButton1.SetTitle ("Click Me", UIControlState.Normal);
objButton1.TouchUpInside += (async (o2, e2) => {
int intCoffeeDispenserResponse = await ShowCoffeeDispenserDialogBox();
//
switch (intCoffeeDispenserResponse)
{
case 0:
UIAlertView objUIAlertView1 = new UIAlertView();
objUIAlertView1.Title = "Coffee Dispenser";
objUIAlertView1.Message = "I hope you enjoy the coffee.";
objUIAlertView1.AddButton("OK");
objUIAlertView1.Show();
break;
case 1:
UIAlertView objUIAlertView2 = new UIAlertView();
objUIAlertView2.Title = "Coffee Dispenser";
objUIAlertView2.Message = "OK - Please come back later when you do.";
objUIAlertView2.AddButton("OK");
objUIAlertView2.Show();
break;
}
});
//
View = objButton1;
private Task<int> ShowCoffeeDispenserDialogBox()
{
TaskCompletionSource<int> objTaskCompletionSource1 = new TaskCompletionSource<int> ();
//
UIAlertView objUIAlertView1 = new UIAlertView();
objUIAlertView1.Title = "Coffee Dispenser";
objUIAlertView1.Message = "Do you want some coffee?";
objUIAlertView1.AddButton("Yes");
objUIAlertView1.AddButton("No");
//
objUIAlertView1.Clicked += ((o2, e2) => {
objTaskCompletionSource1.SetResult(e2.ButtonIndex);
});
//
objUIAlertView1.Show();
//
return objTaskCompletionSource1.Task;
}

Related

Xamarin iOS - Handling Geofence intersection triggers after terminating an app

I am trying to handle geofence intersection triggers when my app is terminated (not background). I want handle the enter/exit triggers from CLLocationManager even if my app is not running. It should wake up my app at the background, do the required processing on enter/exit.
To do this, a lot of places its given that it is possible using background app refresh functionality. I have the following code written but as soon as i terminate the application, it stops hearing the geofence trigger events.
Can anybody please guide me how to handle these events even if application is terminated ?
public async Task StartLocationUpdates()
{
_cts = new CancellationTokenSource();
_taskId = UIApplication.SharedApplication.BeginBackgroundTask("LongRunningTask", OnExpiration);
try
{
if (CLLocationManager.LocationServicesEnabled)
{
LocMgr.DesiredAccuracy = 1;
LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) =>
{
var locations = e.Locations;
};
LocMgr.StartUpdatingLocation();
if (CLLocationManager.LocationServicesEnabled)
{
if (CLLocationManager.Status != CLAuthorizationStatus.Denied)
{
if (CLLocationManager.IsMonitoringAvailable(typeof(CLCircularRegion)))
{
LocMgr.DidStartMonitoringForRegion += (o, e) =>
{
Console.WriteLine("Now monitoring region {0}", e.Region.ToString());
};
LocMgr.RegionEntered += (o, e) =>
{
Instance.Speak("Just entered " + e.Region.ToString());
};
LocMgr.RegionLeft += (o, e) =>
{
Instance.Speak("Just left " + e.Region.ToString());
};
foreach (CLCircularRegion region in RegionList)
{
if (region != null)
{
StopMonitoringRegion(region);
}
LocMgr.StartMonitoring(region);
}
}
else
{
Console.WriteLine("This app requires region monitoring, which is unavailable on this device");
}
LocMgr.Failed += (o, e) =>
{
Console.WriteLine(e.Error);
};
}
else
{
Console.WriteLine("App is not authorized to use location data");
}
}
else
{
Console.WriteLine("Location services not enabled, please enable this in your Settings");
}
}
}
catch (OperationCanceledException)
{
}
}
Thank You in advance.
It seems you have added the privacy keys in the info.plist to enable the location service. If you want to enable it when the app is on background state or terminated, we also should turn on the Background Modes: Capabilities > Background Modes > Location updates.
Then we can initialize the location manager in AppDelegate:
CLLocationManager locationManager;
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
locationManager = new CLLocationManager();
locationManager.Delegate = new MyLocationDelegate();
//enable service when app is on background or terminated
locationManager.PausesLocationUpdatesAutomatically = false;
locationManager.AllowsBackgroundLocationUpdates = true;
locationManager.RequestAlwaysAuthorization();
locationManager.DistanceFilter = 100.0;
locationManager.DesiredAccuracy = 1;
// The important request can awake your app
locationManager.StartMonitoringSignificantLocationChanges();
locationManager.StartUpdatingLocation();
return true;
}
The event LocationsUpdated() will also fire if your location has been changed even your app has been terminated:
public class MyLocationDelegate : CLLocationManagerDelegate
{
public override void LocationsUpdated(CLLocationManager manager, CLLocation[] locations)
{
//Handle your method here
}
}
You can test this with your simulator: Debug > Location > Freeway Drive. Store the locations to your sandbox folder in the event above. Force quit your app, after a few seconds open it, retrieve the file you stored. You will find the whole locations records.

Xamarin-IOS BTLE WroteCharacteristicValue not fired

I have the following code for my IOS implementation, the problem is that the WroteCharacteristicValue event is never fired. Is is being fired on the android side when I connect to the same module. Any ideas what to do?
public void StartUpdates ()
{
// TODO: should be bool RequestValue? compare iOS API for commonality
bool successful = false;
if(CanRead) {
Console.WriteLine ("** Characteristic.RequestValue, PropertyType = Read, requesting read");
_parentDevice.UpdatedCharacterteristicValue += UpdatedRead;
_parentDevice.ReadValue (_nativeCharacteristic);
successful = true;
}
if (CanUpdate) {
Console.WriteLine ("** Characteristic.RequestValue, PropertyType = Notify, requesting updates");
_parentDevice.UpdatedCharacterteristicValue += UpdatedNotify;
_parentDevice.WroteCharacteristicValue += Wrote; // -DP here??
_parentDevice.SetNotifyValue (true, _nativeCharacteristic);
successful = true;
}
Console.WriteLine ("** RequestValue, Succesful: " + successful.ToString());
}
void Wrote(object sender, CBCharacteristicEventArgs e) {
System.Diagnostics.Debug.WriteLine("Characteristic Write Complete!");
this.WriteComplete (this, new CharacteristicReadEventArgs () {
Characteristic = new Characteristic(e.Characteristic, _parentDevice)
});
}
The WroteCharacteristic will only fire if the characteristic writes with response.
You can check it with:
var prop = _nativeCharacteristic.Properties;
if(prop.HasFlag(CBCharacteristicProperties.Write))
{
// Event can be used
}
else if(prop.HasFlag(CBCharacteristicProperties.WriteWithoutResponse))
{
// Event will not fire if WriteWithoutResponse
}
Btw: we provide a plugin for BLE, so you don't have to care about platform sepcific stuff ;) http://smstuebe.de/2016/05/13/blev1.0/

Monotouch : Get user current lcoation

How can i get use location in monotouch?
i'm trying with following code but any events does not fire(AuthorizationChanged & LocationsUpdated)
How should i do?please advise
public Task<CLLocation> test()
{
TaskCompletionSource<CLLocation> objTaskCompletionSource1 = new TaskCompletionSource<CLLocation> ();
CLLocation currentLocation = null;
CLLocationManager LocMgr = new CLLocationManager ();
if (CLLocationManager.LocationServicesEnabled)
{
LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) =>
{
currentLocation = e.Locations [e.Locations.Length - 1];
locationUpdated = false;
if (currentLocation != null && AllAreas != null)
{
LocationDetector.Instance.UpdateCurrentArea (new RLatLng (currentLocation.Coordinate.Latitude, currentLocation.Coordinate.Longitude));
objTaskCompletionSource1.SetResult(currentLocation);
}
else
{
currentLocation = null;
objTaskCompletionSource1.SetResult(currentLocation);
}
};
LocMgr.AuthorizationChanged+= (object sender, CLAuthorizationChangedEventArgs e) => {
Console.WriteLine("AuthorizationChanged Fired");
};
LocMgr.Failed += (object sender, NSErrorEventArgs e) =>
{
};
LocMgr.StartUpdatingLocation ();
}
else
{
currentLocation = null;
objTaskCompletionSource1.SetResult (currentLocation);
Console.WriteLine ("Location services not enabled, please enable this in your Settings");
}
return objTaskCompletionSource1.Task;
}
Chances are you are testing this on iOS 8.
For iOS 8 you now need to request authorization.
So use something like the following in your ViewDidLoad (make LocMgr class scope level - so remove the local instance in your version):-
LocMgr = new CLLocationManager();
if (UIDevice.CurrentDevice.CheckSystemVersion(8,0))
{
LocMgr.RequestWhenInUseAuthorization();
}
Also, in order for the above to work, and for the dialog box to show you also need to add the following entry into your info.plist:-
<key>NSLocationWhenInUseUsageDescription</key>
<value>{some message that will be shown to the end-user}</value>
Update 1:-
Code that I am using:-
In ViewDidLoad:-
LocMgr = new CLLocationManager ();
if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
LocMgr.RequestWhenInUseAuthorization ();
}
UIButton objButton1 = new UIButton (UIButtonType.RoundedRect);
objButton1.SetTitle ("Click Me", UIControlState.Normal);
objButton1.TouchUpInside += (async (o2, e2) => {
CLLocation objLocationInfo = await Test1();
Console.WriteLine("Completed");
});
this.View = objButton1;
And the Test1 function:-
public Task<CLLocation> Test1()
{
TaskCompletionSource<CLLocation> objTaskCompletionSource1 = new TaskCompletionSource<CLLocation> ();
CLLocation currentLocation = null;
if (CLLocationManager.LocationServicesEnabled) {
LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) => {
currentLocation = e.Locations [e.Locations.Length - 1];
locationUpdated = false;
//if (currentLocation != null && AllAreas != null) {
if (currentLocation != null) {
//LocationDetector.Instance.UpdateCurrentArea (new RLatLng (currentLocation.Coordinate.Latitude, currentLocation.Coordinate.Longitude));
objTaskCompletionSource1.SetResult (currentLocation);
} else {
currentLocation = null;
objTaskCompletionSource1.SetResult (currentLocation);
}
};
LocMgr.AuthorizationChanged += (object sender, CLAuthorizationChangedEventArgs e) => {
Console.WriteLine ("AuthorizationChanged Fired");
};
LocMgr.Failed += (object sender, NSErrorEventArgs e) => {
Console.WriteLine("AHH Failed");
};
LocMgr.StartUpdatingLocation ();
} else {
currentLocation = null;
objTaskCompletionSource1.SetResult (currentLocation);
Console.WriteLine ("Location services not enabled, please enable this in your Settings");
}
return objTaskCompletionSource1.Task;
}
Your need these also at class level:-
private bool locationUpdated = false;
private CLLocation currentLocation = null;
private CLLocationManager LocMgr;
Remember you need to edit your info.plist as well.
If you then run the example putting a breakpoint at the Console.WriteLine("Completed"); you should then be able to inspect objLocationInfo and see that it has a location.

UIImagePickerController is showing last clicked image. I am using Monotouch/Xamarin Studio

In my project, when I launch camera first time it works fine. when I launch camera second time, I see image last clicked in view finder. I am not sure what's causing this.
Can anyone please help me here?
following is code block to launch camera:
UIImagePickerController imagePicker = new UIImagePickerController();
// Handle media selected.
var documentsDirectory = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
imagePicker.FinishedPickingMedia += (sender, e) => {
UIImage image = (UIImage)e.Info.ObjectForKey(
new NSString("UIImagePickerControllerOriginalImage"));
if (image != null)
{
this.InvokeOnMainThread(() => {
this.clickedImage.Image = image;
image.SaveToPhotosAlbum(delegate(UIImage img, NSError err){
});
string pngFilename = System.IO.Path.Combine (documentsDirectory, "Photo.png"); // hardcoded filename, overwrites each time
NSData imgData = image.AsPNG();
NSError SaveErr = null;
if (imgData.Save(pngFilename, false, out SaveErr))
{
Console.WriteLine("saved as " + pngFilename);
} else {
Console.WriteLine("NOT saved as" + pngFilename + " because" + SaveErr.LocalizedDescription);
}
});
}
DismissViewController(true,null);
};
// Handle cancellation of picker.
imagePicker.Canceled += (sender, e) => {
DismissViewController(true,null);
};
btnCameraDisplay1.SetTitle("Take Picture", UIControlState.Normal);
btnCameraDisplay1.Font = UIFont.SystemFontOfSize(19);
btnCameraDisplay1.SetTitleColor(UIColor.Black, UIControlState.Normal);
btnCameraDisplay1.TouchUpInside += delegate(object sender, EventArgs e)
{
if(UIImagePickerController.IsSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
{
imagePicker.SourceType = UIImagePickerControllerSourceType.Camera;
imagePicker.AllowsEditing = false;
this.PresentViewController(imagePicker, true,null);
}
else{
alertView = new UIAlertView();
alertView.AddButton("OK");
alertView.Message = "No camera available in this device.";
alertView.AlertViewStyle = UIAlertViewStyle.Default;
alertView.Show();
}
};
I ran into the same Problem. Some searching here on Stackoverflow helped:
It might be a problem with Xamarins implementation of the FinishedPickingMedia Event. (from here https://stackoverflow.com/a/20503817/383658)
Solution:
Switch from the Event System to Delegates as explained here:
https://stackoverflow.com/a/20035698/383658
So basically move your code from:
imagePicker.FinishedPickingMedia += (sender, e) => {}
to your new UIImagePickerControllerDelegate delegates method:
public override void FinishedPickingMedia (UIImagePickerController picker, NSDictionary info)
{}

MonoTouch, ZXing: Redirect to another ViewController after scan

I'm using the ZXing MobileScanner lib with my xamarin project for iOS (MonoTouch).
I set up the scanning, it is working fine, reading my QRCode and having the correct result.
After reading the QRCode I want to show another ViewController. I set a property in the second Controller with the result from scanning and then want to show the controller.
The second view is not shown on screen. No errors, no feedback, simply not shown.
I guess, the MobileScanner builds up its own View (which can be seen in the source of the lib) and adds this to the NavigationController - and that this causes my Controller to stay "behind". Testing with simply redirecting to my controller on button clicks is working fine.
I also tried to "dispose" the view by calling scanner.Cancel(), but this results in
Warning: Attempt to dismiss from view controller <UINavigationController: 0x19a0c60> while a presentation or dismiss is in progress!
Here is my code, any help on how to display my view is appreciated.
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
scanner = new MobileBarcodeScanner(this.NavigationController);
this.btnScan.TouchUpInside += (sender, e) => {
//Tell our scanner to use the default overlay
scanner.UseCustomOverlay = false;
//We can customize the top and bottom text of the default overlay
scanner.TopText = "Ticket vor den Scanner halten";
scanner.BottomText = "Code wird automatisch eingelesen";
//Start scanning
scanner.Scan ().ContinueWith((t) =>
{
//Our scanning finished callback
if (t.Status == System.Threading.Tasks.TaskStatus.RanToCompletion){
string msg = "";
ZXing.Result result = t.Result;
if (result != null && !string.IsNullOrEmpty (result.Text)) {
scanner.Cancel();
if(this.ticketScreen == null) {
this.ticketScreen = new TicketScreen();
}
this.ticketScreen.ticketUrl = result.Text;
this.NavigationController.PushViewController(this.ticketScreen, true);
} else {
msg = "Code nicht erkannt!";
this.InvokeOnMainThread(() => {
var av = new UIAlertView("Fehler!", msg, null, "OK", null);
av.Show();
});
}
}
});
};
}
I use this.InvokeOnMainThread(() and it works fine in my code.
The solution for you is:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
scanner = new MobileBarcodeScanner(this.NavigationController);
this.btnScan.TouchUpInside += (sender, e) => {
//Tell our scanner to use the default overlay
scanner.UseCustomOverlay = false;
//We can customize the top and bottom text of the default overlay
scanner.TopText = "Ticket vor den Scanner halten";
scanner.BottomText = "Code wird automatisch eingelesen";
//Start scanning
scanner.Scan ().ContinueWith((t) =>
{
//Our scanning finished callback
if (t.Status == System.Threading.Tasks.TaskStatus.RanToCompletion){
string msg = "";
ZXing.Result result = t.Result;
if (result != null && !string.IsNullOrEmpty (result.Text)) {
scanner.Cancel();
this.InvokeOnMainThread(() =>
{
if(this.ticketScreen == null) {
this.ticketScreen = new TicketScreen();
}
this.ticketScreen.ticketUrl = result.Text;
this.NavigationController.PushViewController(this.ticketScreen, true);
});
} else {
msg = "Code nicht erkannt!";
this.InvokeOnMainThread(() => {
var av = new UIAlertView("Fehler!", msg, null, "OK", null);
av.Show();
});
}
}
});
};
}

Resources