Xamarin forms geolocator plugin working intermittently - ios

We are trying to use the plugin "Xam.Plugin.Geolocator" in our Xamarin Forms project. The project is currently IOS only.
Our app returns a list of business based on the device users current location. We hit an API to return our JSON formatted list data and the API is functioning correctly.
We would like the list to update whenever the user pulls down, changes tab and when the page initially loads but currently this is only working once or twice in around 100 attempts. I've not found a pattern yet to why it's failing, or indeed when it works.
We set App Properties when the page loads, the tab is selected and the user refreshes like this -
public async void GetLocation()
{
try
{
locator = CrossGeolocator.Current;
if (locator.IsGeolocationAvailable && locator.IsGeolocationEnabled)
{
var position = await locator.GetPositionAsync();
App.Current.Properties["Longitude"] = position.Longitude.ToString();
App.Current.Properties["Latitude"] = position.Latitude.ToString();
}
else
{
await DisplayAlert("Location Error", "Unable to retrieve location at this time", "Cancel");
}
}catch(Exception e)
{
await DisplayAlert("Location Error", "Unable to retrieve location at this time","Cancel");
}
}
We call the above method in the three areas
1) when the page is loaded
public NearbyPage()
{
InitializeComponent();
GetLocation();
SetNearbyBusinesses();
NearbyBusinesses = new List<NearbyBusiness>();
SetViewData();
SetViewVisibility();
}
2) when the tab is clicked
protected override void OnAppearing()
{
base.OnAppearing();
GetLocation();
SetNearbyBusinesses();
NearbyLocationsView.ItemsSource = NearbyBusinesses;
NoLocationsView.ItemsSource = UserMessages;
SetViewVisibility();
}
3) when the user pulls down to refresh
public void RefreshData()
{
if (!CrossConnectivity.Current.IsConnected)
{
NoInternetMessage.IsVisible = true;
return;
}
GetLocation();
NoInternetMessage.IsVisible = false;
SetNearbyBusinesses();
NearbyLocationsView.ItemsSource = NearbyBusinesses;
NoLocationsView.ItemsSource = UserMessages;
SetViewVisibility();
_analyticsService.RecordEvent("Refresh Event: Refresh nearby businesses", AnalyticsEventCategory.UserAction);
}
Can anyone shed some light on what we're doing wrong or have experience with this plugin that can help us resolve this issue?
Thank you
EDIT
By "work", i mean that we'd like it to hit our API with the users current location data and return new results from the API every time the user pulls down to refresh, the page is loaded initially or when they press on a specific tab. Currently it works occasionally, very occasionally.
We can't debug with a phone connected to a macbook, as since we installed the geolocator plugin the app always crashes when connected. The app seems to work ok when deployed to a device, apart from the location stuff. We're currently deploying to test devices via Microsofts Mobile Centre.

Ok, so with the debugger always crashing and being unable to see any stack trace etc we took a few shots in the dark.
We've managed to get this working by adding async to our method signatures down through our code stack. This has resolved the issue and the geo location and refresh is working perfectly.
For example when we changed the above method 3. to refresh the data, it worked perfectly.
public async Task RefreshData()
{
if (!CrossConnectivity.Current.IsConnected)
{
NoInternetMessage.IsVisible = true;
return;
}
GetLocation();
NoInternetMessage.IsVisible = false;
SetNearbyBusinesses();
NearbyLocationsView.ItemsSource = NearbyBusinesses;
NoLocationsView.ItemsSource = UserMessages;
SetViewVisibility();
_analyticsService.RecordEvent("Refresh Event: Refresh nearby businesses", AnalyticsEventCategory.UserAction);
}
We refactored more of that code but adding async was what got it working.
I hope this helps someone else save some time.

Related

Xamarin Forms: How to check if GPS is on or off in Xamarin ios app?

Whenever I am opening my app I need to check the GPS is on or off. If the GPS is off, I need to redirect the user to the location settings page. I have done the android part using the dependency service like below.
ILocSettings
public interface ILocSettings
{
void OpenSettings();
bool isGpsAvailable();
}
Android implementation
[assembly: Dependency(typeof(LocationShare))]
namespace Projectname.Droid.Services
{
public class LocationShare : ILocSettings
{
public bool isGpsAvailable()
{
bool value = false;
Android.Locations.LocationManager manager = (Android.Locations.LocationManager)Android.App.Application.Context.GetSystemService(Android.Content.Context.LocationService);
if (!manager.IsProviderEnabled(Android.Locations.LocationManager.GpsProvider))
{
//gps disable
value = false;
}
else
{
//Gps enable
value = true;
}
return value;
}
public void OpenSettings()
{
Intent intent = new Android.Content.Intent(Android.Provider.Settings.ActionLocat‌​ionSourceSettings);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
}
Finally from the shared project called like below:
//For checking the GPS Status
bool gpsStatus = DependencyService.Get<ILocSettings>().isGpsAvailable();
//For opening the location settings
DependencyService.Get<ILocSettings>().OpenSettings();
For ios how I can I do the same features? I tried like below:
[assembly: Dependency(typeof(LocationShare))]
namespace Projectname.iOS.Serivces
{
class LocationShare : ILocSettings
{
public bool isGpsAvailable()
{
//how to check the GPS is on or off here
}
public void OpenSettings()
{
UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));
}
}
}
Location settings page opening on ios simulators, but don't know how to check the GPS status.
Update1
I have tried the CLLocationManager code and it is not working as expected. It returns true always even if the location is off or on.
OpenSettings() function code (UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));) is also not working as expected, it is redirecting to some other page, I need to open the location settings page if the GPS is off.
Also, I am requesting location permission like below:
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
In android, location permission is asking, but in ios, no permissions are asking.
Update2
I have tried the new codes and getting false value always as GPS status. I have added all the location permission on the info.plist like below:
But location permission is not asking when running the app (not even a single time). I have tried Permissions.LocationWhenInUse instead of Permissions.LocationAlways, but no luck.
Update 3
Following is my complete flow for checking location permission, checking GPS status, and open settings. The permission status value is always Disabled.
//Requesting permission like below:
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
if (status == PermissionStatus.Granted)
{
//Then checking the GPS state like below
bool gpsStatus = DependencyService.Get<ILocSettings>().isGpsAvailable();
if (!gpsStatus)
{
//show a message to user here for sharing the GPS
//If user granted GPS Sharing, opening the location settings page like below:
DependencyService.Get<ILocSettings>().OpenSettings();
}
}
I have tried the below 2 codes for requesting or checking permission. In both cases, the status value is Disabled. If I uninstall the app and reinstall it again, getting the same status and not showing any permission pop-up window.
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
Unlike the Android system, iOS can set the GPS switch separately, and can only get the status of whether the location service is turned on. The rest of the specific positioning method will be left to the iOS system to choose.
At the beginning, we need to have a look at the status of location in iOS:
CLAuthorizationStatus Enum
UIApplicationOpenSettingsURLString: Used to create a URL that you can pass to the openURL: method. When you open the URL built from this string, the system launches the Settings app and displays the app’s custom settings, if it has any.
From now, iOS only support this way to displays the app’s custom settings. There are two helpful discussion, you could have a look. How to jump to system setting's location service on iOS10? and Open Location Settings Not working in ios 11 Objective c?.
If it is redirecting to some other page as follows:
That means your app not do any settings about the location service after installing the app . Therefore, you not need to open the setting page, because it will not show the location service bellow the setting page of your app. Now the CLAuthorizationStatus should be NotDetermined. You could use CLLocationManager.RequestWhenInUseAuthorization to request the permission, the
popup window of location service will show for customer to choose inside the app.
If customer select Don't Allow first time, that means next time opening the app to check the location service that will show Denied status. Now you will need to use UIApplicationOpenSettingsURLString to open the settings page and will see the location service inside the app’s custom settings list.
At last, the final code of LocationShare is as follows:
public class LocationShare : ILocSettings
{
public bool isGpsAvailable()
{
bool value = false;
if ( CLLocationManager.LocationServicesEnabled )
{
if(CLLocationManager.Status == CLAuthorizationStatus.Authorized || CLLocationManager.Status == CLAuthorizationStatus.AuthorizedAlways || CLLocationManager.Status == CLAuthorizationStatus.AuthorizedWhenInUse)
{//enable
value = true;
}
else if (CLLocationManager.Status == CLAuthorizationStatus.Denied)
{
value = false;
OpenSettings();
}
else{
value = false;
RequestRuntime();
}
}
else
{
//location service false
value = false;
//ask user to open system setting page to turn on it manually.
}
return value;
}
public void RequestRuntime()
{
CLLocationManager cLLocationManager = new CLLocationManager();
cLLocationManager.RequestWhenInUseAuthorization();
}
public void OpenSettings()
{
UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));
}
}
Similarly, if CLAuthorizationStatus is Denied (the same as status == PermissionStatus.Unknown in Forms), the following code will not work in Forms.
var status = await Permissions.RequestAsync<Permissions.LocationAlways>();
It only works when CLAuthorizationStatus is NotDetermined. And you'd better request Permissions.LocationWhenInUse instead of Permissions.LocationAlways, this should be the better recommanded option.
============================Update #2================================
I have modified the above code, and you will see that if CLLocationManager.LocationServicesEnabled is false, we only can ask user to redirect to the system setting page to turn on the service manually. Because from iOS 10, iOS not supports to navigate to system setting page from non-system app.
============================Update #3======================================
If location service is enabled, when using UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString)); method you will see the similar screenshot as follows:
The Loaction option will show in the list.

Xamarin Forms Plugin.Share not working for unknown reason

We have an image with a gesture recognizer, which calls the CrossShare.Current.Share method. On Android this works fine but on iOS it does not. No error is thrown and there doesn't seem to be any issues, but the share sheet does not appear and from the user's point of view nothing happens when you click the button.
Have I missed some permissions or something somewhere?
This is my method;
async void On_Share(object sender, EventArgs e)
{
if (CrossConnectivity.Current.IsConnected)
{
var message = "Check out this";
var title = "Share this";
await CrossShare.Current.Share(new ShareMessage { Text = message, Title = title}, new ShareOptions { ExcludedUIActivityTypes = new[] { ShareUIActivityType.PostToFacebook } });
}
else
{
NoInternetLabel.IsVisible = true;
}
}
It doesn't throw any errors and I can step through the method fine - it definitely hits the Share line. This problem is only showing on iOS, Android has no issues.
EDIT: Seems to be working fine (we've tried doing it natively as well - without plugin) but now I'm getting Warning: Attempt to present on whose view is not in the window hierarchy!

How to hide amazon ads in unity

So I integrated amazon mobile ads into my unity/ios project. I have it all working to where I hide the ads every time a scene changes. Every time I open a scene, the ad shows. So it's all working fine except when you change scenes really quickly. I don't want ads in the main game as it obstructs the view of the users. Every time you get to the retry scene, if you quickly switch from that scene right before an ad loads, that ad will get stuck on the next scene which makes another ad show on top of it. Every time a scene changes it should be hiding the ad not matter how fast you change scenes. Is there any way to make sure it hides the ad if an ad is shown? I'm using the code below:
void Start() {
mobileAds = AmazonMobileAdsImpl.Instance;
ApplicationKey key = new ApplicationKey();
key.StringValue = iosKey;
mobileAds.SetApplicationKey(key);
ShouldEnable enable = new ShouldEnable();
enable.BooleanValue = true;
mobileAds.EnableTesting(enable);
mobileAds.EnableLogging(enable);
Placement placement = new Placement();
placement.Dock = Dock.BOTTOM;
placement.HorizontalAlign = HorizontalAlign.CENTER;
placement.AdFit = AdFit.FIT_AD_SIZE;
response = mobileAds.CreateFloatingBannerAd(placement);
string adType = response.AdType.ToString();
long identifer = response.Identifier;
newResponse = mobileAds.LoadAndShowFloatingBannerAd(response);
bool loadingStarted = newResponse.BooleanValue;
}
void OnDestroy() {
mobileAds.CloseFloatingBannerAd(response);
response = null;
mobileAds = null;
newResponse = null;
}
When did you download the Unity Plugin? There were some issues in an early version of the plugin that this sounds like (the whole, one ad loading over top of another thing). If you have not updated it recently, try downloading the latest version from Amazon and see if the issue still occurs.
The close ad API
mobileAds.CloseFloatingBannerAd(response);
will only work if the ad is already loaded. You need to register for the ad loaded event. If the scene is destroyed, then you would close the ad when the ad loaded event tiggers.
You can register for AdLoaded event as follows, Documentation
using com.amazon.mas.cpt.ads;
bool sceneDestroyed = false; //tracks if scene is destroyed
//Obtain object used to interact with the plugin
IAmazonMobileAds mobileAds = AmazonMobileAdsImpl.Instance;
// Define event handler
private void EventHandler(Ad args)
{
if (sceneDestroyed)
{
mobileAds.CloseFloatingBannerAd(response);
}
else
{
//Do some other job
}
}
//Register for an event
mobileAds.AddAdLoadedListener(EventHandler);
void OnDestroy()
{
sceneDestroyed = true;
}

GWT Window.confirm() triggered by onchange of ValueListBox crashing Safari on iPad iOS 7.0.6

I recently received a support ticket that some of our web app's functionality is crashing safari on the iPad. This functionality had no problems prior to the latest iOS 7.0.6 update. We have a few GWT ValueListBoxes that change the DOM when their values are changed. Prior to making the changes, we present the user with a Window.confirm() message to inform them of the effects the changes will have and ask whether or not they would still like to proceed. Since the update, the confirm choices do nothing and Safari crashes. This is only happening on the iPad. The functionality works fine on the desktop browsers (IE, Chrome, Firefox, Safari and the Chrome mobile emulator), but crashes safari on the iPad. Is anyone else having this issue?
Here's a screenshot of the crash:
And here's a sample of the code:
this._view.isPrimaryFoodGen().addValueChangeHandler(new ValueChangeHandler<Boolean>()
{
#Override
public void onValueChange(final ValueChangeEvent<Boolean> event)
{
#SuppressWarnings("unchecked")
ValueListBoxWithOldValue<Boolean> vlb = (ValueListBoxWithOldValue<Boolean>)event.getSource();
if (confirmQuestionChange() ){
changeGroupAndQuestions(CONSTANTS.PRIMARY_FOOD, event.getValue());
}
else {
vlb.setValue(vlb.getOldValue());
}
}
});
public boolean confirmQuestionChange()
{
if (!this._view.isImageCriteriaQuestionsVisible())
{ //questions aren't currently visible
return true;
}
boolean confirmed = Window.confirm("Changing this response will delete image data already collected. Do you wish to proceed?");
return confirmed;
}
Any help on a solution for preventing the crash on the iPad would be greatly appreciated. I have tried focusing on another element prior to calling Window.confirm() in hopes that the overlay and the ValueListBox choices would be removed to stop any JS conflicts, but it hasn't worked.
Am I at the mercy of Apple until the next update fixes this?
Or is there a viable solution?
OK, so it turns out that since I couldn't find a fix to continue using Window.confirm(), I had to implement a solution by changing the onValueChange() and confirmQuestionChange() methods to use a manually created DialogBox instead of Window.confirm(). It isn't the optimal solution, but Safari does not crash on the iPad anymore and users can get their work done. Here are the code changes:
this._view.isPrimaryFoodGen().addValueChangeHandler(new ValueChangeHandler<Boolean>()
{
#Override
public void onValueChange(final ValueChangeEvent<Boolean> event)
{
confirmQuestionChange(CONSTANTS.PRIMARY_FOOD, event);
}
});
public void confirmQuestionChange(final String question, ValueChangeEvent<Boolean> event)
{
final ValueListBoxWithOldValue<Boolean> vlb = (ValueListBoxWithOldValue<Boolean>)event.getSource();
if (!this._view.isImageCriteriaQuestionsVisible()) //questions aren't currently visible, can change them no problem
{
changeGroupAndQuestions(question, vlb.getValue());
}
else{
//the following fix was put in place for issues with Safari on the iPad OPS-76
final DialogBox dialogBox = new DialogBox();
dialogBox.setHTML("<center>Changing this response will delete<br />image data already collected.<br />Do you wish to proceed?</center>");
dialogBox.setAnimationEnabled(true);
Button yesButton = new Button("YES");
Button noButton = new Button("NO");
HorizontalPanel dialogHPanel = new HorizontalPanel();
dialogHPanel.setWidth("100%");
dialogHPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
dialogHPanel.add(noButton);
dialogHPanel.add(yesButton);
noButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
vlb.setValue(vlb.getOldValue());
dialogBox.hide();
}
});
yesButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
changeGroupAndQuestions(question, vlb.getValue());
dialogBox.hide();
}
});
// Set the contents of the Widget
dialogBox.setWidget(dialogHPanel);
dialogBox.setPopupPosition(180, 425);
dialogBox.show();
}
}
Here's a screenshot:
As you can see, the ValueListBox options close before the DialogBox appears and the screen no longer locks.

Strange behaviour of Geolocator.GetGeopositionAsync() after first launch

I'm writing Windows Phone 8 app that needs to get location of device (do not track changes, just get location). I added next code to the method OnNavigatedTo() of my start page but after launching app, the progress indicator does not hide even after 10 seconds timeout. But if I navigate to another page and then go back, everything works fine. This happens on the emulator, I don't have a real device. What am I doing wrong?
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
if(_geoPosition == null)
{
try
{
var geolocator = new Geolocator();
geolocator.DesiredAccuracyInMeters = 50;
_progressIndicator = new ProgressIndicator
{
IsIndeterminate = true,
Text = "Getting current location, please wait...",
IsVisible = true
};
SystemTray.SetIsVisible(this, true);
SystemTray.SetProgressIndicator(this, _progressIndicator);
_geoPosition = await geolocator.GetGeopositionAsync(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10));
_progressIndicator.IsVisible = false;
SystemTray.SetIsVisible(this, false);
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("Location is disabled in phone settings");
}
}
}
Thanks!
UPD: just tried to add this code to empty project and it works fine. Tried to comment out some parts of OnNavigatedTo that I did not include to the snippet and found out that the reason somewhere in initialization of data source for this page. I'm sorry for false alarm.
Your code works fine for me, try the classic restart VS and the projecy!
The code should work, tested it with an emulator and a device (nokia 820).
Best of luck

Resources