I am trying to showToast when the phone leaves or enter the geofenced location (which is set elsewhere and passed in). The issue is that when the app is in the background the trigger does not occur and I don't see the showToast message. I am changing the location manually using an emulator on my PC.
Background Tasks> Location is set under the app manifest.
This is the code I am using to build the Geofence and backgroundtask
//Creates Geofence and names it "PetsnikkerVacationFence"
public static async Task SetupGeofence(double lat, double lon)
{
await RegisterBackgroundTasks();
if (IsTaskRegistered())
{
BasicGeoposition position = new BasicGeoposition();
position.Latitude = lat;
position.Longitude = lon;
double radius = 8046.72; //5 miles in meters
Geocircle geocircle = new Geocircle(position, radius);
MonitoredGeofenceStates monitoredStates = MonitoredGeofenceStates.Entered | MonitoredGeofenceStates.Exited;
Geofence geofence = new Geofence("PetsnikkerVacationFence", geocircle, monitoredStates, false);
GeofenceMonitor monitor = GeofenceMonitor.Current;
var existingFence = monitor.Geofences.SingleOrDefault(f => f.Id == "PetsnikkerVacationFence");
if (existingFence != null)
monitor.Geofences.Remove(existingFence);
monitor.Geofences.Add(geofence);
}
}
//Registers the background task with a LocationTrigger
static async Task RegisterBackgroundTasks()
{
var access = await BackgroundExecutionManager.RequestAccessAsync();
if (access == BackgroundAccessStatus.Denied)
{
}
else
{
var taskBuilder = new BackgroundTaskBuilder();
taskBuilder.Name = "PetsnikkerVacationFence";
taskBuilder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
taskBuilder.SetTrigger(new LocationTrigger(LocationTriggerType.Geofence));
taskBuilder.TaskEntryPoint = typeof(Petsnikker.Windows.Background.GeofenceTask).FullName;
var registration = taskBuilder.Register();
registration.Completed += (sender, e) =>
{
try
{
e.CheckResult();
}
catch (Exception error)
{
Debug.WriteLine(error);
}
};
}
}
static bool IsTaskRegistered()
{
var Registered = false;
var entry = BackgroundTaskRegistration.AllTasks.FirstOrDefault(keyval => keyval.Value.Name == "PetsnikkerVacationFence");
if (entry.Value != null)
Registered = true;
return Registered;
}
}
}
This code is where I monitor the state of the geofence.
This is where the Entry point in the appxmanifest is pointing
public sealed class GeofenceTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
var monitor = GeofenceMonitor.Current;
if (monitor.Geofences.Any())
{
var reports = monitor.ReadReports();
foreach (var report in reports)
{
switch (report.NewState)
{
case GeofenceState.Entered:
{
ShowToast("Approaching Home",":-)");
break;
}
case GeofenceState.Exited:
{
ShowToast("Leaving Home", ":-)");
break;
}
}
}
}
//deferral.Complete();
}
private static void ShowToast(string firstLine, string secondLine)
{
var toastXmlContent =
ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var txtNodes = toastXmlContent.GetElementsByTagName("text");
txtNodes[0].AppendChild(toastXmlContent.CreateTextNode(firstLine));
txtNodes[1].AppendChild(toastXmlContent.CreateTextNode(secondLine));
var toast = new ToastNotification(toastXmlContent);
var toastNotifier = ToastNotificationManager.CreateToastNotifier();
toastNotifier.Show(toast);
Debug.WriteLine("Toast: {0} {1}", firstLine, secondLine);
}
}
After looking at your code, it seems that your code is correct.
In order to fire the Geofence Backgroundtask to show the toast information, please make sure the following things:
1) Please make sure that you have done all the necessary configuration in the Package.appxmanifest for registering the BackgroundTask, for example you have set the correct EntryPoint and added the “Location” capabilities.
For the detailed information, you can try to compare your Package.appxmanifest with the official sample Geolocation’s Package.appxmanifest.
Please also check: Create and register a background task and Declare background tasks in the application manifest.
2) Please make sure that you know how to set the location in the Emulator manually for simulating the phone leave or enter the geofenced location. For more information about how to set location in the emulator, please check the following article:
https://msdn.microsoft.com/library/windows/apps/dn629629.aspx#location_and_driving .
3) Please make sure that your second position in your emulator is not really far away from the geofences that you have defined in the first time, because the emulator behaves like a real device, and the device doesn’t expect to suddenly move from New York to Seattle. Or the BackgroundTask will not be fire immediately.
4) Background tasks for geofencing cannot launch more frequently than every 2 minutes. If you test geofences in the background, the emulator is capable of automatically starting background tasks. But for the next subsequent background tasks, you need to wait for more than 2 minutes.
Besides, I will recommend you refer to the following article about how to use the Windows Phone Emulator for testing apps with geofencing:
https://blogs.windows.com/buildingapps/2014/05/28/using-the-windows-phone-emulator-for-testing-apps-with-geofencing/ .
Thanks.
Related
So... I'm working with a third-party library (zkemkeeper.dll) to make a winform applicaction to manage attendance devices. I'm trying to get several devices connected and attach some events to listen for some actions they will fire. Everithing it's fine running all sync. I run some methods and for each device I try to connect like this:
public bool connect(Device myDevice)
{
bool result=false;
if (!myDevice.isConn)
{
myDevice.zkDevice = null;
myDevice.zkDevice = new CZKEMClass();
myDevice.zkDevice.MachineNumber = myDevice.id;
if (myDevice.password.HasValue)
{
myDevice.zkDevice.SetCommPassword(myDevice.password.Value);
}
try
{
result = myDevice.zkDevice.Connect_Net(myDevice.ip, myDevice.port);
myDevice.error = result ? "" : "Could not connect to device";
}
catch (Exception ex)
{
myDevice.error = ex.Message;
result = false;
}
if(result)
{
//Bind events
if (myDevice.zkDevice.RegEvent(myDevice.id, 1))
{
myDevice.zkDevice.OnAttTransactionEx += new _IZKEMEvents_OnAttTransactionExEventHandler(device_OnTransactionEx);
}
}
myDevice.zkDevice.EnableDevice(myDevice.id, true);
myDevice.zkDevice.EnableClock(1);
}
return result;
}
My main problem is that this take a couple of seconds to connect to each device depending on the state of the network, so if I have 50 or 100 device the interface will freeze every time a device is connected or reconnected and this will make a huge delay.(besides that the application must always be functional)
Ok to solve that I use this:
private async Task connectAllAsync()
{
List<Task<bool>> lstTasks = new List<Task<bool>>();
foreach (var device in lstDevices)
{
lstTasks.Add(Task.Run(() => connect(device)));
}
var arrayComplete =await Task.WhenAll(lstTasks);
int countErr=arrayComplete.ToList().Where(n => n == false).Count();
if(countErr>0)
{
timerReconnect.Enabled = true;
}
}
After this the interface doesn't freeze,my connection to all devices is faster,even if I try to interrogate any device for information the device respond BUT when I try to trigger any event these doesnt fire, I dont know where is my mistake I think it could be related to atach the event on another thread or something like that... Help me give me some way to go, thanks in advance.
Edit: Im also tried making the "connect" function async an make it await for the Connect_Net response (which is part of the third party code)
You have an uncaptured closure. Try this:
foreach (var device in lstDevices)
{
var captured = device;
lstTasks.Add(Task.Run(() => connect(captured)));
}
Or just use LINQ to do it all:
lstTasks = lstDevices.Select
(
device => Task.Run
(
() => connect(device)
)
);
A totally different approach would get rid of Task.Run() and use Parallel.ForEach instead. If you use this approach, you'd have to store the results in a thread-safe container like ConcurrentBag<>.
var results = new ConcurrentBag<bool>();
Parallel.ForEach
(
lstDevices,
device =>
{
results.Add(connect(device));
}
);
int countErr = results.Count( x => x == false );
Or you could just use a counter:
volatile int countErr = 0;
Parallel.ForEach
(
lstDevices,
device =>
{
var ok = results.Add(connect(device));
if (!ok) Interlocked.Increment(ref countErr);
}
);
In the way of learning BLE programming using C#, I'm trying to write a winform application that should detec BLE devices and use them as proximity sensors. For this purpose I've applied the BluetoothAdvertisementWatcher class, filtering the advertisements with a provided signal strength. The problem is the detection seems to be quite crappy... Even with the device at no distance from my PC, the listener often returns a RSSI value of -127, wich stands for "no detection" as I've understood. I've tried setting the OutOfRangeTimeout to 10 seconds and, even if things get better, problems are still heavy. The major issue is the detection can flows without interruption just for about a bunch of seconds, then the debugger signals me a thread exiting with code 0, and the listener can't receive new advertisements (with the device ever aside the pc) for even 30-40 seconds, then it restarts and so it loops.
Do you know if is this how it is intended to work or if am I missing something? Is there a way, eventually, to intercept this interruption and immediately restart the listener?
This is how I initialize the watcher:
public BLEScanner(short maxDBRange, TimeSpan outOfRangeTimeout) {
this.InRange = maxDBRange;
this.OutOfRange = (short)(this.InRange + BLEScanner.BUFFER_RANGE);
this.OutOfRangeTimeout = outOfRangeTimeout;
this.watcher = new BluetoothLEAdvertisementWatcher();
this.watcher.SignalStrengthFilter.InRangeThresholdInDBm = this.InRange;
this.watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = this.OutOfRange;
this.watcher.SignalStrengthFilter.OutOfRangeTimeout = this.OutOfRangeTimeout;
this.watcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromSeconds(1);
this.watcher.ScanningMode = BluetoothLEScanningMode.Active;
}
While this is the event handler:
private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs) {
var deviceAddress = eventArgs.BluetoothAddress;
BluetoothLEDevice device = await BluetoothLEDevice.FromBluetoothAddressAsync(deviceAddress);
UpdateUiDelegate update = new UpdateUiDelegate(
(dev, args) => {
if (eventArgs.RawSignalStrengthInDBm == -127) {
this.form.spyLabel.BackColor = System.Drawing.Color.Red;
this.form.nameLabel.Text = "(none)";
this.form.addressLabel.Text = "(none)";
this.form.rssiLabel.Text = "(none)";
this.form.connectedLabel.Text = device.ConnectionStatus.ToString();
} else {
this.form.spyLabel.BackColor = System.Drawing.Color.Green;
this.form.nameLabel.Text = device.Name;
this.form.addressLabel.Text = device.BluetoothAddress.ToString();
this.form.rssiLabel.Text = eventArgs.RawSignalStrengthInDBm.ToString();
this.form.connectedLabel.Text = device.ConnectionStatus.ToString();
}
});
this.form.Invoke(update, device, eventArgs);
Console.Write(eventArgs.RawSignalStrengthInDBm + " ");
}
Any help is appreciated!
I am using Geoposition and a Postition changed event to grab Coordinates of the device location.
private async void StartGpsMonitoring()
{
if (locator == null)
{
locator = new Geolocator();
}
if (locator.LocationStatus == PositionStatus.Disabled)
{
//throw new Exception();
MessageDialog noGpsDialog = new MessageDialog("Location services are disabled, please enable location services");
noGpsDialog.Commands.Add(new UICommand("Location Settings", new UICommandInvokedHandler(this.CommandInvokedHandler), 0));
noGpsDialog.Commands.Add(new UICommand("Cancel", new UICommandInvokedHandler(this.CommandInvokedHandler), 1));
await noGpsDialog.ShowAsync();
}
if (locator != null)
{
//locator.MovementThreshold = 3;
locator.ReportInterval = 1;
locator.DesiredAccuracy = PositionAccuracy.High;
locator.PositionChanged +=
new TypedEventHandler<Geolocator,
PositionChangedEventArgs>(locator_PositionChanged);
}
}
private async void locator_PositionChanged(Geolocator sender, PositionChangedEventArgs e)
{
string speed = string.Empty;
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Geoposition geoPosition = e.Position;
if (e.Position.Coordinate.Speed != null)
{
speed = e.Position.Coordinate.Speed.Value.ToString(); // always 5.8
}
geolocation = geoPosition.Coordinate.Point.Position.Latitude.ToString() + " " +
geoPosition.Coordinate.Point.Position.Longitude.ToString() + "Speed = " +
speed;
var textBlockStatus =
ControlHelper.FindChildControl<TextBlock>(JourneyTrackerSection, "TextBlockStatus") as TextBlock;
textBlockStatus.Text = geolocation;
});
}
I am also trying to get the speed value. But when using the emulator I am always getting 5.8 regardless of if I have speed limit/walking/biking set on the emulator, and still get 5.8 from a static position.
Can anybody shed some light as to why? Is it just the emulator? Would I get an accurate result if I used a real device?
Its hard to develop a location speed application where I have to run out side every time I want to debug/run it.
Any help much appreciated.
Thought I would post some details as to what I have managed to find out in case anyone comes across this in the future. Looks like this is because it is running on the emulator. Managed to come across some limited details about using the geopositioning code and some details about it being hardware specific. After I have managed to get hold of a windows phone for testing the emulator cannot do the speed. Works perfect on an actual device.
This is extremely annoying by Microsoft. means that every time I need to test my app I have to go out driving. Rendering the GPS emulator completely useless!
The new API for Geolocation in Windows Universal (Windows 10 apps) has a new way for allowing access to a user's location.
Starting in Windows 10, call the RequestAccessAsync method before accessing the user’s location. At that time, your app must be in the foreground and RequestAccessAsync must be called from the UI thread.
I'm running some very simple code for Geolocation, on the UI thread as shown below, but I get location permission "denied" every time and no prompt to allow location permissions. Has anyone else run into this? How do I get the prompt to allow location permissions for geolocation in a Windows 10 app?
Geolocation method
private async Task<ForecastRequest> GetPositionAsync()
{
try
{
// Request permission to access location
var accessStatus = await Geolocator.RequestAccessAsync();
if (accessStatus == GeolocationAccessStatus.Allowed)
{
// Get cancellation token
_cts = new CancellationTokenSource();
CancellationToken token = _cts.Token;
// If DesiredAccuracy or DesiredAccuracyInMeters are not set (or value is 0), DesiredAccuracy.Default is used.
Geolocator geolocator = new Geolocator { DesiredAccuracyInMeters = _desireAccuracyInMetersValue };
// Carry out the operation
_pos = await geolocator.GetGeopositionAsync().AsTask(token);
return new ForecastRequest()
{
Lat = (float)_pos.Coordinate.Point.Position.Latitude,
Lon = (float)_pos.Coordinate.Point.Position.Longitude,
Unit = Common.Unit.us
};
}
else
throw new Exception("Problem with location permissions or access");
}
catch (TaskCanceledException tce)
{
throw new Exception("Task cancelled" + tce.Message);
}
finally
{
_cts = null;
}
}
Where it's called:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ForecastViewModel vm = await ForecastViewModel.BuildViewModelAsync(await GetPositionAsync());
DataContext = vm.Forecast;
uxForecastList.Visibility = Visibility.Visible;
}
You have to set the "Location" capability. You can do this in the appmanifest.
In the screenshot you find where to set the capability:
Find more info here:
https://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.geolocation.geolocator.aspx (Scroll all down to find info on capabilities)
System Information
Windows 10 Technical Preview (build 9926)
Visual Studio Community 2013Attempting to debug on:
[AT&T] Lumia 635 (Windows 10 Technical Preview for phones build 9941 w/ Lumia Cyan)
[AT&T] Lumia 1520 (Windows Phone 8.1 with Lumia Denim and PfD)
[Unlocked] BLU Win Jr (Windows Phone 8.1 with PfD)
[Verizon] Lumia Icon (Windows Phone 8.1 with Lumia Denim and PfD)
I trying to get location services working in my app. Previously, I had Visual Studio throw the error. It was an ArgumentException with the message "Use of undefined keyword value 1 for event TaskScheduled in async". Googling didn't turn up any solutions.
Here is the code:
Geolocator Locator = new Geolocator();
Geoposition Position = await Locator.GetGeopositionAsync();
Geocoordinate Coordinate = Position.Coordinate;
When I could get the error to be thrown, the exception was thrown on the 2nd or 3rd line in the sample above.
I simplified the original code to try and fix it, but this is the original:
Geolocator Locator = new Geolocator();
Geocoordinate Coordinate = (await Locator.GetGeopositionAsync()).Position.Coordinate;
The entire app works when debugging, but crashes almost instantaneously otherwise.
This is a Windows 8.1 Universal project, focusing on the phone project.
Thanks in advance
EDIT: As requested, here is the full method:
private static bool CheckConnection()
{
ConnectionProfile connections = NetworkInformation.GetInternetConnectionProfile();
bool internet = connections != null && connections.GetNetworkConnectivityLevel() == NetworkConnectivityLevel.InternetAccess;
return internet;
}
public static async Task<double> GetTemperature(bool Force)
{
if (CheckConnection() || Force)
{
Geolocator Locator = new Geolocator();
await Task.Yield(); //Error occurs here
Geoposition Position = await Locator.GetGeopositionAsync();
Geocoordinate Coordinate = Position.Coordinate;
HttpClient Client = new HttpClient();
double Temperature;
Uri u = new Uri(string.Format("http://api.worldweatheronline.com/free/v1/weather.ashx?q={0},{1}&format=xml&num_of_days=1&date=today&cc=yes&key={2}",
Coordinate.Point.Position.Latitude,
Coordinate.Point.Position.Longitude,
"API KEY"),
UriKind.Absolute);
string Raw = await Client.GetStringAsync(u);
XElement main = XElement.Parse(Raw), current_condition, temp_c;
current_condition = main.Element("current_condition");
temp_c = current_condition.Element("temp_C");
Temperature = Convert.ToDouble(temp_c.Value);
switch (Memory.TempUnit)
{
case 0:
Temperature = Convertions.Temperature.CelsiusToFahrenheit(Temperature);
break;
case 2:
Temperature = Convertions.Temperature.CelsiusToKelvin(Temperature);
break;
}
return Temperature;
}
else
{
throw new InvalidOperationException("Cannot connect to the weather server.");
}
}
EDIT 2: I've asked for help on Twitter, and received a reply asking for a repro project. I recreated the major portion of the original app, but I could not get the error. However, errors may occur for you so here's the project.
EDIT 3: If it helps at all, here are the exception details:
System.ArgumentException occurred
_HResult=-2147024809
_message=Use of undefined keyword value 1 for event TaskScheduled.
HResult=-2147024809
IsTransient=false
Message=Use of undefined keyword value 1 for event TaskScheduled.
Source=mscorlib
StackTrace:
at System.Diagnostics.Tracing.ManifestBuilder.GetKeywords(UInt64 keywords, String eventName)
InnerException:
Having checked this and this, I believe this is a bug in .NET async/await infrastructure for WinRT. I couldn't repro it, but I encourage you to try the following workaround, see if it works for you.
Factor out all asynchronous awaitable calls from OnNavigatedTo into a separate async Task method, e.g. ContinueAsync:
async Task ContinueAsync()
{
Geolocator Locator = new Geolocator();
Geoposition Position = await Locator.GetGeopositionAsync();
Geocoordinate Coordinate = Position.Coordinate;
// ...
var messageDialog = new Windows.UI.Popups.MessageDialog("Hello");
await messageDialog.ShowAsync();
// ...
}
Remove async modifier from OnNavigatedTo and call ContinueAsync from OnNavigatedTo like this:
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(
() => ContinueAsync(),
CancellationToken.None, TaskCreationOptions.None, scheduler).
Unwrap().
ContinueWith(t =>
{
try
{
t.GetAwaiter().GetResult();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
throw; // re-throw or handle somehow
}
},
CancellationToken.None,
TaskContinuationOptions.NotOnRanToCompletion,
scheduler);
Let us know if it helps :)
Updated, apparently, the bug is somewhere in the TPL logging provider, TplEtwProvider. You can see it's getting created if you add the below code. So far, I couldn't find a way to disable this event source (either directly or via Reflection):
internal class MyEventListener : EventListener
{
protected override void OnEventSourceCreated(EventSource eventSource)
{
base.OnEventSourceCreated(eventSource);
if (eventSource.Name == "System.Threading.Tasks.TplEventSource")
{
var enabled = eventSource.IsEnabled();
// trying to disable - unsupported command :(
System.Diagnostics.Tracing.EventSource.SendCommand(
eventSource, EventCommand.Disable, new System.Collections.Generic.Dictionary<string, string>());
}
}
}
// ...
public sealed partial class App : Application
{
static MyEventListener listener = new MyEventListener();
}