Windows Phone ContactPicker won't await properly - c#

Although this has been posted before on StackOverflow but i think none of those reflect my issue and none of those solutions work for me either. So i'm developing a Windows Phone app and my workflow is a bit like this:
App starts
ContactPicker opens up
User selects one or multiple contacts
Based on how many contacts he selected, that many PivotItems are added into the Pivot.
My code is as follows:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// TODO: Prepare page for display here.
// TODO: If your application contains multiple pages, ensure that you are
// handling the hardware Back button by registering for the
// Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
// If you are using the NavigationHelper provided by some templates,
// this event is handled for you.
SelectContacts();
}
private async Task SelectContacts()
{
var picker = new ContactPicker();
picker.DesiredFieldsWithContactFieldType.Add(ContactFieldType.PhoneNumber);
ContactsList = (List<Contact>)await picker.PickContactsAsync();
DisplayContacts();
}
private void DisplayContacts()
{
if (ContactsList != null)
{
foreach (var item in ContactsList)
{
PivotItem pivotItem = new PivotItem();
pivotItem.Header = item.FirstName.ToString();
ContentRoot.Items.Add(pivotItem);
}
}
}
According to me, in SelectContacts() method, the app should wait at the await call and once it gets back the list of contacts, than it should execute the DisplayContacts() method but its not working. I've tried multiple other variations of this code and they aren't working either.

await the SelectContacts() method and add the DisplayContacts() method beneath it. Remove the DisplayContacts() method from SelectContacts()
await SelectContacts();
DisplayContacts();

I don't know the complete reason why but i figured it out that since i was making the PickContactsAsync() call in the OnNavigatedTo() event, that is why it wasn't working as expected. Once i moved the PickContactsAsync() call into the PageLoaded() event handler, it started working as usual.

Related

How to get notifications to appear right after page loads? (Xamarin, LocalNotifications Plugin)

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.

send data to running async function xamarin

Working with Xamarin Android and C# -
I want to have a download function, that just download the next item (url) from the list if the first with index 0 is finished. While Downloading, the user should be able to extend the list (add new urls for download).
My idea was to have one void OnButtonClick() (for user input) and one custom aysnc void Download() function, as well the possibility to use the "share function" (intent) to send the link directly. It is working BUT only if the user does not uses the "share function" in another app (see here: Intent.GetStringExtra). If the App gets open via this intent, the download loop gets overwritten completely. Is there a way to avoid this "bug" or another solution for a download que?
protected override void OnCreate() //gets called if activity starts
{
string catchedLink = Intent.GetStringExtra(Intent.ExtraText);
if (!String.IsNullOrEmpty(catchedLink))
{
button.Text = catchedLink;
}
}
public OnButtonClick()
{
urlList.Add(Button.Text);
}
private aysnc void Download()
{
if(IsDownloading) return;
IsDownloading = true;
do
{
await DownloadSomethingFromTheInternet(); //Let's say these two function need 2 mins to complete -
await SafeItToStorage(); //But after one minute the user adds a secound url for download
//so this loop needs to run again (see below)
urlList.Remove(urlList[0]);
} while (urlList.Count >= 1) //see here
IsDownloading = false;
}
Please leave a comment if more details are needed.
Okay it's working now:
What I did:
Save the links on the device with Preferences.Set(); not only just with a List
Added LaunchMode = Android.Content.PM.LaunchMode.SingleTask to AndroidMainfest.xml (in <activity [...] />)
Used for intent-input following code:
protected override void OnNewIntent(Intent myIntent)
{
base.OnNewIntent(myIntent);
string catchedLink = myIntent.GetStringExtra(Intent.ExtraText);
if (!String.IsNullOrEmpty(catchedLink))
{
AddVideoToQue(catchedLink);
}
}```
Thanks #Leo Zhu - MSFT for his comments and help!

How to detect call hang up in windows phone 10

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.

Loading screen for GoogleMaps activity

I'm trying to implement a loading screen since GoogleMaps sometimes takes ~2 seconds to load. I tried using a RelativeLayout and putting the ImageView above the map fragment and making it ViewStates.Gone once the map loads. However, I noticed that the loading is happening on base.OnCreateBundle and not on SetUpMap() and now I'm clueless how to implement it.
private GoogleMap mMap;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Map);
mSplash = FindViewById<ImageView>(Resource.Id.splash);
SetUpMap();
}
private void SetUpMap()
{
if (mMap == null)
{
FragmentManager.FindFragmentById<MapFragment(Resource.Id.map).GetMapAsync(this);
}
}
public void OnMapReady(GoogleMap googleMap)
{
mSplash.Visibility = ViewStates.Gone;
mMap = googleMap;
mMap.MapType = GoogleMap.MapTypeSatellite;
(...)
}
If I'm not mistaken, OnMapReady is meant to be used to verify that Google Play Services is installed and active on the device. As the documentation puts it:
If Google Play services is not installed on the device, the user will
be prompted to install it, and the onMapReady(GoogleMap) method will
only be triggered when the user has installed it and returned to the
app.
OnMapReady Documentation
Alternatively, the OnMapLoaded function is meant to be called after the map has finished loading its tiles.
Called when the map has finished rendering. This will only be called
once. You must request another callback if you want to be notified
again.
OnMapLoaded Documentation
You can implement the OnMapLoaded function by first setting the callback once you have a reference to the map
mMap.setOnMapLoadedCallback(this);
Finish by simply overriding the OnMapLoaded function and handling it accordingly
#Override
public void onMapLoaded() {
if (mMap != null) {
mSplash.Visibility = ViewStates.Gone;
}
}
Remember that if you need to load the map again and need to display another splash screen, you will first need to set the callback again, as onMapLoaded is only called once per callback.

How to update TextBlock when async HttpRequest is finished?

I'm working on an app and I'm restructuring my code.
On my MainPage.xaml.cs I have a TextBlock and a ListBox. I have separate file (Info.cs) that handles the HttpRequest to get the information that I need to load.
The HttpRequest in Info.cs gets information from a weather API. When it gets all the information it puts the info in a ObservableCollection.. This ObservableCollection is bind to the ListBox.
Now, I'd like to update the TextBlock when the HttpRequest is finished, to show the user that all the information has been loaded.
How can I achieve this?
MainPage.xaml.cs:
WeatherInfo weatherInfo = new WeatherInfo();
weatherInfo.getTheWeatherData();
DataContext = weatherInfo;
WeatherListBox.ItemsSource = weatherInfo.ForecastList;
StatusTextBlock.Text = "Done.";
In the Info.cs I have a Dispatcher to fill the ForecastList:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
ForecastList.Clear();
ForecastList = outputList;
}
What happens now is that the TextBlock instantly changes to "Done!" (doh, its Async!) but how can I change this? So it 'waits' on the ListBox to be updated? Unfortunatly there is no 'ItemsSourceChanged' event in Windows Phone.
I suggest to use the new async+await power from C# 5.0, This is actually a good practice to use async programming in WP8.
assuming you have control of getTheWeatherData() method, and that you can mark it as async method that returns Task, you will be able to call it using await modifier.
await will not block the UI, and will cause the next code lines to be executed only after the task is done.
WeatherInfo weatherInfo = new WeatherInfo();
await weatherInfo.getTheWeatherData();
DataContext = weatherInfo;
WeatherListBox.ItemsSource = weatherInfo.ForecastList;
StatusTextBlock.Text = "Done.";
Edit:
It is supported on WP 8, and on WP 7.5 through Microsoft.Bcl.Async Nuget Package
If async programming is not an option,
you can always create a callback event in WeatherInfo class that will be signaled inside getTheWeatherData(), and register to it on the UI.
One option looks as follows:
public static void DoWork(Action processAction)
{
// do work
if (processAction != null)
processAction();
}
public static void Main()
{
// using anonymous delegate
DoWork(delegate() { Console.WriteLine("Completed"); });
// using Lambda
DoWork(() => Console.WriteLine("Completed"));
}
Both DoWork() calls will end with calling the callback that is passed as a parameter.

Categories