I'm developing UWP app which will assist people with public transportation in my city. It uses geofencing to notify when the device is near some station. All is working fine, but slow. On my lumia 830 it took 4-5 seconds to prepare data for all 250 geofences and create them. Since, geofencemonitor runs under system and can store my geofences even when my app is terminated, I thought it would be smart to not recreate all geofences again and again on every app startup, but create them once and then just reuse them.
But I cant figure out how. When I start my app, Geofencemonitor.Current.Count is 250 and GeofenceMonitor.Current.Status is Ready, but it never fires Geoface state changed event.
My problem:
I m trying to reuse geofences in geofencemonitor. When I run my app and during startup I dont call Geofencemonitor.Current.Geofences.Clear(); and then create and add all my geofences -> geofence state change event is never fired.
Edit: It seems, that geofence status change fires only once after I add geofence to geofencemonitor, independently on if the app was restarted or not. But "used" geofence still remains in geofence monitor. Does geofence have some supended state? Am I missing some geofence property?
Here is how I create geofence:
var geofence = new Geofence(id, geocircle, mask, false, dwellTime);
GeofenceMonitor.Current.Geofences.Add(geofence);
My questions:
"Since, geofencemonitor runs under system and can store my geofences even when my app is terminated" - Is this really true?
If my first questions is true, and it is possible to reuse geofences, what am I doing wrong or do you know some place where I can go deeper into Geofencing in UWP?
Thanks for advice.
Here is how I listen geofences state change event:
ItsValueIs250 = Geofencemonitor.Current.Geofences.Count;
ItsValueIsReady = Geofencemonitor.Current.Status;
public async void OnGeofenceStateChanged(GeofenceMonitor sender, object e)
{
var reports = sender.ReadReports();
await Dispatcher.DispatchAsync( () =>
{
foreach (GeofenceStateChangeReport report in reports)
{
GeofenceState state = report.NewState;
Geofence geofence = report.Geofence;
if (state == GeofenceState.Entered)
{
stationInRange.Add(geofence.Id);
StationInRangeCount = stationInRange.Count().ToString();
}
else if (state == GeofenceState.Exited)
{
stationInRange.Remove(geofence.Id);
}
}
});
}
And maybe this method which I call during startup.
async public Task<string> GeomonitorInitialize()
{
string message = string.Empty;
var accessStatus = await Geolocator.RequestAccessAsync();
switch (accessStatus)
{
case GeolocationAccessStatus.Allowed:
int i = 0;
i = GeofenceMonitor.Current.Geofences.Count;
return message;
case GeolocationAccessStatus.Denied:
message = "GeoDenied";
return message;
case GeolocationAccessStatus.Unspecified:
message = "GeoError";
return message;
default:
message = "Default";
return message;
}
}
I will add any piece of code I use as you will wish. Thank you.
Related
I've been stuck on this all day, so I'm going to post everything I've been able to find today that might be useful to helping me, it will be a long post. I'm having 2 issues that I believe are related to the same problem. First, let me explain what I am doing. I have 3 Winforms combo boxes that are bound to lists of all of the devices found by MMDeviceEnumerator. Two output device boxes, one input device. I am using the MMDeviceEnumerator to register a callback for whenever the devices are changed, removed, or a default device is set. The callback fires an event that then invokes a delegate to the form thread to re-enumerate the devices into combo boxes. It looks like this:
public void OnDefaultDeviceChanged(DataFlow dataFlow, Role deviceRole, string defaultDeviceId)
{
Devices.OnDevicesUpdated();
}
//The handler called by this event:
private void UpdateDeviceSelectors(object? sender = null, EventArgs? e = null)
{
Invoke(delegate ()
{
int primaryIndex = Devices.PrimaryOutput + 1, secondaryIndex = Devices.SecondaryOutput + 2, microphoneIndex = Devices.Microphone + 1;
Devices.Refresh();
try
{
SecondaryOutputComboBox.SelectedIndex = secondaryIndex;
PrimaryOutputComboBox.SelectedIndex = primaryIndex;
MicrophoneSelectComboBox.SelectedIndex = microphoneIndex;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
});
}
Now for the two issues I have. The first one involves a semi-random crash that leads back to NAudio.Wasapi.dll, where a System.ExecutionEngineException is thrown. It is kind of easy to reproduce. All I do is change the values of the combo boxes, switch the default devices around, and it will randomly crash.
The second issue occurs when another part of my code is involved. I have a microphone injector, which redirects a WaveInEvent that records a selected input device to a WaveOutEvent, like a loopback. Here is the relevant code for this:
public void Start()
{
if (Soundboard.Devices.SecondaryOutput == -2) return;
micStream = new WaveInEvent();
micStream.BufferMilliseconds = 50;
micStream.WaveFormat = new WaveFormat(44100, WaveIn.GetCapabilities(Soundboard.Devices.Microphone).Channels);
micStream.DeviceNumber = Soundboard.Devices.Microphone;
WaveInProvider waveIn = new(micStream);
var volumeSampleProvider = new VolumeSampleProvider(waveIn.ToSampleProvider());
volumeSampleProvider.Volume = 1 + Settings.Default.MicrophoneGain;
virtualCable = new WaveOutEvent();
virtualCable.DesiredLatency = 150;
virtualCable.DeviceNumber = Soundboard.Devices.SecondaryOutput;
virtualCable.Init(volumeSampleProvider);
micStream.StartRecording();
virtualCable.Play();
}
public void Stop()
{
try
{
if (micStream != null && virtualCable != null)
{
micStream.Dispose();
micStream = null;
virtualCable.Dispose();
virtualCable = null;
}
}
catch
{
micStream = null;
virtualCable = null;
}
}
In the delegate mentioned earlier, I am calling the Stop method of the Mic Injector and then the Start method to refresh the WaveIn and WaveOut devices to use the current device numbers so users do not see a device selected when a different device is being used. When this happens, the program, rather than crashing instantly and inconsistently, always hangs and has to be killed from the task manager. I am certain these 2 problems are related to the same root cause, but I have no idea what that root cause may be. Switching to Wasapi, DirectSound, or ASIO won't work because they lack certain functionalities I need, so I would really like to get this working still using Wave streams.
I've tried to find different ways to detect the device changes, assuming it is an issue deep inside NAudio, but I just can't find anything else. For the second problem specifically, I have moved the calls to the Mic Injector around thinking it may be a threading issue or something, but it didn't work or change the behavior.
I have a project that needs notifications about upcoming events to show when the user opens the main page, and I'm having problems with async and await (I think). I am using the LocalNotifications Nuget plugin, because that's the only one I was allowed to use for this project. I have some methods that are intended to check for and display notifications. I have tried loading them from my App file, My MainPage.Xaml.cs's OnAppearing method, and from my MainPageViewModel.cs, and the closest that I've gotten to something working is with a combination of creating the Notifications as a class, adding them from the ViewModel and displaying them in the OnAppearing Method of the code behind. The code works right up until the if statement's body in the OnAppearing Method tries to execute and then it just stops and continues on with displaying the main page, but never showing the notifications. I am really new to using asynchronous programming, as well as Xamarin, so I'm pretty confused. This is my relevant code.
//Code starts in MainPageViewModel.cs
public MainPageViewModel()
{
Task.Run(async() => await CheckNotifications());
}
private async Task CheckNotifications()
{
await DatabaseService.Init();
var eventList = await DatabaseService.db.Table<Event>().ToListAsync();
if (eventList.Count > 0)
{
foreach (Event event in eventList)
{
if (event.NotifyStartDate && (event.StartDate.Date == DateTime.Today))
{
Notifications notification = new Notifications
{
Name = $"{event.Name}",
NotifyDate = event.StartDate,
Occurrence = "Starting"
};
await DatabaseService.AddNotification(notification);
}
if (event.NotifyEndDate && (event.EndDate.Date == DateTime.Today))
{
Notifications notification = new Notifications
{
Name = $"{event.Name}",
NotifyDate = event.EndDate,
Occurrence = "Ending"
};
await DatabaseService.AddNotification(notification);
}
}
}
}
This all seems to work fine, it creates a Notification with the event that starts today and adds it to the Notifications Table in the database. Next it moves to my MainPage Code Behind's OnAppearing method--> which is an async void method and I can't figure out any other way to do this, because I get an error that "MainPage.OnAppearing() return type must be void" if I try to make it a Task.
protected override async void OnAppearing()
{
//List fills fine here and holds my Test Event starting today information with a count of 1
var notifications = await DatabaseService.db.Table<Notifications>().ToListAsync();
if (notifications.Count > 0)
{
foreach(Notifications notification in notifications)
{
CrossLocalNotifications.Current.Show("Reminder", $"Event {notification.Name} is {notification.Occurrence} Today" +
$"on {notification.NotifyDate}");
//code stops at this line and continues the rest of the program without executing notification
}
}
base.OnAppearing();
}
My Init, FillSampleData, and AddNotifications methods in the database are all async tasks, and they all await other methods, but the problem seems to occur at OnAppearing. To add to that I am Navigating to the Main Page from App.Xaml.cs Synchrounously, because when I tried to make it an asynchronous method it got ignored there.
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
Everywhere else in my app the navigation awaits. I don't know if I'm just doing the notifications all wrong, if I'm just doing Asynchronous programming all wrong, or a little bit of both. Any help would be much appreciated. Thanks guys.
I'm creating a background process in iOS to send location updates to a server, but I'm having trouble controlling it from the main application. Mostly, I'm having trouble killing the service. My app uses a button that start/stops the background process, and also figuring out if the process is still running.
I'm using two methods in a class, a StartListening uses a timer to start location updates, and StopListening that kills the timer. But the I can't always kill the background process, especially if the app has been switched off. I don't have the problem with Android since the API takes care of starting and stopping the location process, but iOS, I have to do this manually.
public void StartListening()
{
//class setup here
if(CLLocationManager.LocationServicesEnabled)
{
locationManager.DesiredAccuracy = 10;
locationManager.StartUpdatingLocation();
taskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
{
this.timer.Dispose();
});
timer = new Timer(async (o) =>
{
CLLocation location = null;
while(location == null)
{
location = locationManager.Location;
}
if (location != null)
{
//handle location
}
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(UpdateInterval));
UIApplication.SharedApplication.EndBackgroundTask(taskId);
}
}
public void StopListening()
{
this.locationManager.StopUpdatingLocation();
if(taskId != 0)
{
if (this.timer != null)
{
this.timer.Change(Timeout.Infinite, Timeout.Infinite);
this.timer.Dispose();
}
}
}
I'm expecting the StopLocation to stop the background process, and in testing, it works fine, but when I actually try to use the app for a while, it seems it'll "forget" about the background process and not kill it properly. Does anyone have a suggestion on why this is/how to fix this? Any help is appreciated.
Add the following code
if(CLLocationManager.LocationServicesEnabled)
{
locationManager.DesiredAccuracy = 10;
locationManager.RequestWhenInUseAuthorization();
locationManager.AllowsBackgroundLocationUpdates = false;
locationManager.StartUpdatingLocation();
//...
}
RequestWhenInUseAuthorization only works when the foreground operation mode is switched to the background operation mode. For example, if the App switches to the background operation mode, the agent method didUpdateLocations will not continue.
And do not forget add the privacy in info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>xxxx</string>
I want to programmatically detect when call hang up in UWP app for windows phone 10.
I see Caller ID sample and see Communication blocking and filtering sample too.
But I don't find simple solution for detect when call hang up. I found CallHistoryChanged trigger and I think my complex solution is read call history when call history changed trigger and get last incoming call.
Is there any simple solution for detect call hang up?
Is my solution is correct?
Is there a better solution than my solution?
Is there any simple solution for detect call hang up?
Is my solution is correct? Is there a better solution than my solution?
As far as I know, there is no other simple solution for detect calls' hang up.
There is an event PhoneCallManager.CallStateChanged, which can also detect hang up like below:
private bool callCame = false;
PhoneCallManager.CallStateChanged += PhoneCallManager_CallStateChanged;
private async void PhoneCallManager_CallStateChanged(object sender, object e)
{
if (callCame&&(!PhoneCallManager.IsCallActive))
{
//do something
}
if (PhoneCallManager.IsCallIncoming)
{
callCame = true;
}
}
But I don't think it's better than CallHistoryChanged trigger. And it won't get you the last Hang up phone's number either.
Is my solution is correct?
So yes, your solution is correct.
Update: There is no trick here. Register the BackgroundTask:
private void btnClick_Click(object sender, RoutedEventArgs e)
{
var taskRegistered = false;
var exampleTaskName = "MyBackgroundTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == exampleTaskName)
{
taskRegistered = true;
break;
}
}
if (!taskRegistered)
{
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "PhoneCallBackground.Class1";
builder.SetTrigger(new PhoneTrigger(Windows.ApplicationModel.Calls.Background.PhoneTriggerType.CallHistoryChanged, false));
builder.Register();
}
}
And don't forget to register it in the appxmanifest file:
The Run Method will be fired after you hang up, if you registered the BackgroundTask. PhoneCallManager can not be registered for BackgroundTask. You need to set it as the default PhoneCall App, if you want to use this.
Update2:
I checked the demo, you are still using PhoneTriggerType.CallOriginDataRequest to register the background task. Please change it to PhoneTriggerType.CallHistoryChanged. And also make sure the registered background task is unregistered after you making any change to your codes.
Here is the demo that I modified: CallHistoryChangeTest.
I am trying to detect how long the user has a been inactive within the app or system wide with the Windows 8 Store apps API.
I look into the system trigger User Away however that just tells you when it goes idle. Doesn't let you specify a certain time. I also took a look at Pointer Pressed and try to detect tap or touch events; but this does not work because I am using a webview and unable to capture PointerPressed events through a web view.
Is there any way to detect if a user has been idle for an X amount of time within the app or system wide? Any help is appreciated, thank you!
I ended up detecting key presses and mouse down events with javascript. Inside the LoadCompleted event, I inject javascripts by using AW_WebView.InvokeScript("eval", new string[] { scriptsString });
Key Press Script:
window.document.body.onkeydown = function(event){
window.external.notify('key_pressed');
};
Mouse Down Scripts:
document.onmousedown = function documentMouseDown(e){
window.external.notify('mouse_down');
}
And you can add additional scripts for additional user events.
When a mouse down or key press is detected, window.external.notify("keypress or mouse down") gets executed. This message gets "received" in my WebView_ScriptNotify event. When I receive a message from my WebView, I set a timer. If the timer is already set, it cancels it and starts the timer again. When the timer has finished, some code gets executed.
private void SetTimer(int time)
{
if (!TimerEnabled)
{
return;
}
else
{
if (DelayTimer != null)
{
DelayTimer.Cancel();
DelayTimer = null;
}
//the timeout
TimeSpan delay = TimeSpan.FromSeconds(time);
bool completed = false;
DelayTimer = ThreadPoolTimer.CreateTimer(
(source) =>
{
//
// Update the UI thread by using the UI core dispatcher.
//
Dispatcher.RunAsync(
CoreDispatcherPriority.High,
() =>
{
//
// UI components can be accessed within this scope.
//THIS CODE GETS EXECUTED WHEN TIMER HAS FINISHED
});
completed = true;
},
delay,
(source) =>
{
//
// TODO: Handle work cancellation/completion.
//
//
// Update the UI thread by using the UI core dispatcher.
//
Dispatcher.RunAsync(
CoreDispatcherPriority.High,
() =>
{
//
// UI components can be accessed within this scope.
//
if (completed)
{
// Timer completed.
}
else
{
// Timer cancelled.
}
});
});
}
}
Hope this helps someone! I know this is not a perfect method of getting this done, but for the time being this worked for me.