Geolocation.GetLastKnownLocationAsync() sometimes returns null - xamarin

This is on iOS 12.1.4 on an iPhone 6s. Usually Geolocation.GetLastKnownLocationAsync() works but sometimes it doesn't. It's the exact same code but if I sit here and press my "get latitude and longitude" button over and over eventually Geolocation.GetLastKnownLocationAsync() spits out a null.
Do you know why this happens and how I might handle it? Perhaps put it in a loop that tries ten times, waiting a second between each try?
var location = await Essentials.Geolocation.GetLastKnownLocationAsync(); // works most of the time but sometimes it doesn't work.
This is my proposed work around:
Essentials.Location location = null;
for(var i = 0; i < 10; i++)
{
location = await Essentials.Geolocation.GetLastKnownLocationAsync();
if(location == null)
{
Thread.Sleep(1000);
}
else
{
break;
}
}

First, it is really bad practice to use Thread.Sleep (unless you are not on the main/UI loop) as you are hanging the run loop, if you really need a delay, use await Task.Delay(.... Also CLLocationManager on iOS is running on the main loop and if you are blocking it, the message pump is hung and the location manager manager can not report back to the app.
"Spamming" CLLocationManager.Location (which Essentials is using on iOS) can (and will) result in null returns due to OS rate limiting updates (mainly a battery conservation measure) and if the OS is powering up the GPS radio to update its location, this method will timeout on from the OS, thus report nil back to GetLastKnownLocationAsync and thus you get a return of null.
CLLocationManager.Location on iOS is meant for a quick low-power return from the OS to app as is updated upon app launch, device reboot, etc... not every time you call it.
You can get the last known location of the device by calling the GetLastKnownLocationAsync method. This is often faster then doing a full query, but can be less accurate.
Otherwise you should be using GetLocationAsync in order to do a full GPS power up to obtain an updated accurate location.
To query the current device's location coordinates, the GetLocationAsync can be used. It is best to pass in a full GeolocationRequest and CancellationToken since it may take some time to get the device's location.
Typically I recommend using GetLastKnownLocationAsync as a quick way to get the general area of the user knowing that this might also return null. Then proceed to do a GetLocationAsync (passing both a GeolocationRequest and CancellationToken instance) in the background and update the app accordingly upon the more accurate and recent position.
re: https://learn.microsoft.com/en-us/xamarin/essentials/geolocation?tabs=ios

Related

MacOS not responding to MPRemoteCommandCenter commands in the background

I am writing an application for my own purposes that aims to get play pause events no matter what is going on in the system. I have gotten this much working
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.togglePlayPauseCommand.isEnabled = true
commandCenter.togglePlayPauseCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
print("Play Pause Command")
return .success
}
commandCenter.nextTrackCommand.isEnabled = true
commandCenter.nextTrackCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
print("NextTrackCommand")
return .success
}
commandCenter.previousTrackCommand.isEnabled = true
commandCenter.previousTrackCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
print("previousTrackCommand")
return .success
}
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
print("playCommand")
return .success
}
MPNowPlayingInfoCenter.default().playbackState = .playing
Most of those methods are there because apparently you will not get any notifications without having nextTrackCommand or previousTrackCommand or playCommand implemented.
Anyways my one issue is that as soon as you open another application that uses audio these event handlers stop getting called and I cant find a way to detect and fix this.
I would normally try doing AVAudioSession things to state this as a background application however that does not seem to work. Any ideas on how I can get playpause events no matter what state the system is in?
I would like to be able to always listen for these events OR get an indication of when someone else has taken control of the audio? Perhaps even be able to re-subscribe to these play pause events.
There's an internal queue in the system which contains all the audio event subscribers. Other applications get on top of it when you start using them.
I would like to be able to always listen for these events
There's no API for that but there's a dirty workaround. If I understand your issue correctly, this snippet:
MPNowPlayingInfoCenter.default().playbackState = .paused
MPNowPlayingInfoCenter.default().playbackState = .playing
must do the trick for you if you run it in a loop somewhere in your application.
Note that this is not 100% reliable because:
If an event is generated before two subsequent playbackState state changes right after you've switched to a different application, it would still be catched by the application in the active window;
If another application is doing the same thing, there would be a constant race condition in the queue, with unpredictable outcome.
References:
Documentation for playbackState is here;
See also a similar question;
See also a bug report for mpv with a similar
issue (a pre-MPRemoteCommandCenter one, but still very valuable)
OR get an indication of when someone else has taken control of the audio
As far as I know there's no public API for this in macOS.

In WP7, data is not saved immediately after writing to IsolatedStorage

I encountered this problem while polishing my WP7 application.
Though I follow Microsoft's guidelines to store game state when it's being deactivated, I'd also like to save some data in runtime.
The reason for this is that when the battery is removed from device, no deactivation / closing callbacks are triggered.
The problem with this comes when the user walks through the game and accidentally removes the battery from her device - all game progress is lost.
That's why I do save game state at some intermediate checkpoints, but I have noticed that data is not stored immediately. This is my "save" function:
public void SaveAppModelToIsolatedStorage()
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
using (var stream = new IsolatedStorageFileStream(APPMODEL_DATAFILE,
FileMode.Create,
FileAccess.Write,
store))
{
var serializer = new XmlSerializer(typeof(AppModel));
try
{
serializer.Serialize(stream, AppModel);
}
catch (Exception ex)
{
Debug.WriteLine("Cant serialize AppModel:" + ex.Message);
}
}
}
After it's been called, if I remove the battery within a number of seconds (not sure how many but always less than 30), the application ends up with lost game progress. If I wait before removing battery, the data would be saved successfully. This behavior is observed on various WP7 phones.
I also tried serialization into a string buffer and then writing that string buffer to the file by calling stream.Write(), but the result is the same. Also, stream.Flush() doesn't seem to have an effect.
Is this behavior a platform feature?
Is it fine in terms of Microsoft certification for Marketplace apps?
Or is there a way to save data immediately?
Just call stream.Close when you need flush your data.
Also, try calling stream.Flush(true);
This may be interesting regarding performance of Isolated Storage : forums.create.msdn.com/forums/p/71708/71708.aspx

What makes SwitchDesktop not work right after a user unlocks it session?

I have a program that switches desktop and start a new process on it. When the process exits, the parent process restores the original desktop.
For testing purposes, I put a button in a plain win32 app that triggers the switch. It works, and closing the launched process (notepad), I go back to the original desktop.
In that same program, I have called WTSRegisterSessionNotification to receive a notification when a session is unlocked (WTS_SESSION_UNLOCK). I receive it.
But when I try to switch desktops in WTS_SESSION_UNLOCK message handler, SwitchDesktop fails and GetLastError is 0. The documentation says that last error is usually not set by SwitchDesktop.
Funny enough, if I put my call to switch desktop in a for loop, it works on the 5th iteration.
In short, this does not work :
case WM_WTSSESSION_CHANGE:
if(wParam == WTS_SESSION_UNLOCK)
{
SwitchDesktop(a_valid_desktop_handle);
}
break;
But this ugly hack works :
case WM_WTSSESSION_CHANGE:
if(wParam == WTS_SESSION_UNLOCK)
{
for(int i=0; i<10; ++i)
{
if(SwitchDesktop(a_valid_desktop_handle))
{
//This will work when i == 5, maybe 6.
break;
}
}
}
break;
Setting a timer (to exit the message loop) also works, but it is just a more convoluted form of loop with regards to this problem. SwitchDesktop will work on after a handfull of WM_TIMER messages. It looks like constant time, although I did not measure it.
MSDN documentation for SwitchDesktop mentions that this will fail with a custom Userinit process, which I use. But getting the name of the current desktop just before the switch :
wchar_t name[512];
GetUserObjectInformation(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME, name, sizeof(name)/sizeof(*name), 0);
OutputDebugString(name);
Gives me default all the time. And since GetLastError is 0, not 5 (access denied) I am pretty sure the secure desktop is gone before I receive the WTS_SESSION_UNLOCK notification.
I known I can't switch desktop while the screen is locked, but is there a "grace period" after the desktop is unlocked in which I can't call SwitchDesktop ?
When the desktop gets locked, it switches to another desktop which is reserved for this purpose. It is quite possible that when you receive the message, that desktop is still in control and you're not allowed to switch because you're not running in the current desktop.
I can't test it right now but I would put the call to SwitchDesktop not on WTS_SESSION_UNLOCK but on WTS_CONSOLE_CONNECT. From what I gather WTS_SESSION_UNLOCK occurs first and then your get WTS_CONSOLE_CONNECT which would correspond to what you see with the "constand time"...
SwitchDesktop fails (with error 0) because (accordingly to MSDN) it belongs to a window station not (yet) visible. There is no user notification I know of that says "the HWINSTA become visible".

Selenium Firefox Open timeout

Using Windows 2008, C#, Firefox 3.5.1, Selenium RC (v1.0.1)
When it works, this code executes very quickly and the page loads within .5 seconds.
However, the session always seems to fail after 3 - 5 iterations. The open command will cause a window to be spawned, but no page to be loaded. Eventually a timeout exception is returned. The page has not actually timed out. Instead, it is as though the request for a URL has never reached the browser window.
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
var s = new DefaultSelenium("localhost", 4444, "firefox", "http://my.server");
s.Start();
s.SetSpeed("300");
s.Open("/");
s.WaitForPageToLoad("30000");
s.Type("//input[contains(#id, '_username')]", "my.test");
s.Type("//input[contains(#id, '_password')]", "password");
s.Stop();
}
}
}
I have a similar set up (Firefox 3.6.15, Selenium RC 1.0.1, but on WinXP and using the Python libraries) and I am working with a couple of sites - one site is naturally prone to timeouts in normal use (e.g. by a human user) whereas the others typically are not. Those that aren't appear a little slower but the one that is prone to timeouts is significantly slower when run via RC than by a person - it won't always timeout but the incidence is much much more common.
My limited mental model for this is that somehow the extra steps RC is doing (communicating with the browser, checking what it sees in the returned pages etc etc) are somehow adding a bit to each step of the page loads and then at some point they will push it over the edge. Obviously this is overly simplified, I just haven't had time to properly investigate.
Also, I do tend to notice that the problem gets worse over time, which fits a little with what the OP has seen (i.e. working the first time but not after 3 - 5 attempts). Often a reboot seems to fix the issues, but without proper investigation I can't tell why this helps, perhaps it is somehow freeing up memory (the machine is used for other things), getting allocated to a different one of our company's proxies or something else I haven't considered.
So... not much of a full answer here (a comment would have been more appropriate, but my login isn't able to yet), but at least it reinforces that you're not the only one. Periodic restarts are an annoying thing to need to do, but in the absence of any smarter analysis and answers, maybe they'd be worth a shot?
I was facing the same problem .This is because open method of DefaultSelenium has timeout of 30000ms, so it waits for 30s for your page to load. You can try this trivial solution.
//selenium is DefaultSelenium instance as private member of the class
boolean serverStartTry = false;
int tryCount =1;
while((!serverStartTry) && tryCount <= Constants.maxServerTries){
try{
this.selenium.open(ReadConFile.readcoFile("pageName"));
System.out.println("Server started in try no: "+tryCount);
serverStartTry =true;
}catch (SeleniumException e) {
System.out.println("Server start try no: "+tryCount );
System.out.println("Server Start Try: "+ serverStartTry);
serverStartTry = false;
tryCount++;
}
}
if(!serverStartTry){
System.out.println("Server Not started, no. of attempts made: "+tryCount);
System.exit(0);
}
I've solved using:
selenium.setTimeout("60000");
before open instruction.

How can I get notified of a system time change in my Cocoa application?

I have a Cocoa application that records datestamps on events. I need to know when the system time is reset and by how much, but I can't seem to fine a notification anywhere that tells me such a thing has happened. This change could happen because NTP reset the clock or because the user reset (e.g. from System Preferences). It would be great if there was just a NSNotification I could register to receive, but I'm open to any suggestions.
Apple added in NSSystemClockDidChangeNotification, part of NSDate, in Snow Leopard (10.6). There doesn't appear to be a way to do it in Leopard (10.5) or earlier. Per the Apple NSDate docs:
Posted whenever the system clock is changed. This can be initiated by a call to settimeofday() or the user changing values in the Date and Time Preference panel. The notification object is null. This notification does not contain a userInfo dictionary.
This doesn't appear to tell you "how much" time has changed. You could possibly calculate that by periodically (say, every 5 seconds in a NSTimer) capturing the system time with [NSDate date], saving it into a variable, and then after NSSystemClockDidChangeNotification fires, grab the new date and compare the two together using NSDate's timeIntervalSinceDate: method to get the difference.
Not millisecond or even second accurate, but pretty close.
EDIT: See this post. You could possibly use the UpTime() C command to grab the system uptime in CPU tics (which you can later convert to seconds). You could use this to figure out by how much time has changed (assuming no system restart or sleep). This works even if the system clock is changed by the user or network time protocol.
If some on is looking for solution know system date change change event from 10.4
OSStatus DateChangeEventHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData)
{
NSLog(#"Event received!\n");
return 0;
}
- (void)SystemTimeChangeHandler
{
EventTypeSpec eventType;
eventType.eventClass = kEventClassSystem;
eventType.eventKind = kEventSystemTimeDateChanged;
EventHandlerUPP eventHandlerUPP =
NewEventHandlerUPP(DateChangeEventHandler);
EventHandlerRef eventHandlerRef = NULL;
(void)InstallApplicationEventHandler(
eventHandlerUPP,
1,
&eventType,
self,
&eventHandlerRef);
}
Time moves constantly. A notification every time the current time changed would be a constant, CPU-soaking stream of notifications.
What you need to do is get the current time in your event handler—the one that receives the events you're datestamping. You get the current time by calling [NSDate date].
I don't think there's a single way to do that because of the different mechanisms by which the time could change. But it wouldn't be very expensive (too expensive? Don't know, have you profiled it yet? ;-) to set an NSTimer once a second to check the time and compare it with the previous value. If it's not advanced by about a second, something interesting happened and you can notify your audit object.

Resources