As I’ve previously blogged, I created a Xamarin Forms application to submit pictures of my receipts to the company I work for. But if I’d publish the app in that state, the whole world could send in receipts on my behalf. So I needed to add authentication. Because the company uses Office 365, I’d decided to add Azure Active Directory.

There are a lot of resources about Adal (Active Directory Authentication Library) Some call it Azure Adal.NET

You can read about it on the Github page

https://github.com/AzureAD/azure-activedirectory-library-for-dotnet

It is also very clear in the readme.md that you should in fact upgrade to MSAL.Net

MSAL.NET is the new authentication library to be used with the Microsoft identity platform”

Searching for Xamarin.Forms and Msal.Net did not gave me that much options, so I decided to blog about it.

MSAL stands for MicroSoft Authentication Library. The Github is https://github.com/AzureAD/microsoft-authentication-library-for-dotnet

It’s for Oauth2 and OpenID connect.

I started with adding an application registration in the azure portal under azure active directory at “app registrations”

I switched to “Single tenant” at the authentication tab/page to make sure that only people from within Partech are allowed.

Further more you need to write down the app (client) id and the directory (tenant) id.

Next was the code integration:

1. add nuget ‘Microsoft.Identity.Client’ to the Xamarin shared app

2. make sure your app.xaml.cs looks like this:


public static IPublicClientApplication PCA;
private const string applicationClientId = "clientidhere";
public static string[] Scopes = { "User.Read" };
public static string Username = string.Empty;

public static object ParentWindow { get; set; }


public App()
{
	PCA = PublicClientApplicationBuilder.Create(applicationClientId)
		.WithTenantId("tenantid here")
		.WithRedirectUri($"msal{applicationClientId}://auth")
		.WithIosKeychainSecurityGroup("com.microsoft.adalcache")
		.Build();


	InitializeComponent();

	MainPage = new MainPage();
}

3. to the mainpage I added an override of OnAppearing which called an async CheckAuth

4. add this code to checkauth:


AuthenticationResult authResult = null;
IEnumerable accounts = await App.PCA.GetAccountsAsync();
try
{
	IAccount firstAccount = accounts.FirstOrDefault();
	authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
						  .ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
	try
	{
		authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
								  .WithParentActivityOrWindow(App.ParentWindow)
								  .ExecuteAsync();
	}
	catch (Exception ex2)
	{
		DisplayAlert("Acquire token interactive failed. See exception message for details: ", ex2.Message, "Dismiss").RunSynchronously();
	}
}

if (authResult != null)
{
	await GetHttpContentWithTokenAsync(authResult.AccessToken);
}

5. for the rest of the code to retrieve the users given name, surname etc. you should check the GitHub link I mentioned previously.

To do more with azure AD use the Graph explorer

Good luck coding!

Post on LinkedIn

There was an email earlier this year about a change in declarations from my employer. You can’t just drop receipt on someone’s desk anymore, but since we are in a digital era, we should make a picture of it and send it by mail. That sounds fair. But that would require me to remember the constraints, like letting know for which client it is and to which address I should send it to. So I decided that it needed automation.

Because some coworkers have iPhones and some have Android, I decided to go for this approach:

architecture

Xamarin Forms (Android, iOs) post a picture of a receipt to Azure Functions in the Azure cloud which sends it to Sendgrid. (Because Azure Functions cannot send mail)

Here are the steps I took to make a simple camera app:

1. File, new project, new Xamarin Forms project (no web api)

2. Add new project to solution (Azure Functions)

3. Add the nuget package ‘Xam.Plugin.Media’ to the shared/main xamarin project

4. Add UI code:



        

5. Add code to cs (code behind file)

I had three class variables (type string) filePath, filename, url (url to your azure function) I forgot that the emulator is a vm so you can’t use the localhost if you are testing the Azure Function but you should use your LAN ip.


private async void takePhoto_Clicked(object sender, EventArgs e)
{
	if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
	{
		await DisplayAlert("No Camera", ":( No camera avaialble.", "OK");
		return;
	}
	filename = DateTime.Now.ToString("yyyyMMdd-") + Guid.NewGuid() + ".jpg";

	var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
	{
		PhotoSize = PhotoSize.Medium,
		Directory = "Sample",
		Name = filename
	});

	if (file == null)
		return;

	filePath = file.Path;

	image.Source = ImageSource.FromStream(() =>
	{
		var stream = file.GetStream();
		file.Dispose();
		return stream;
	});
	sendPhoto.IsEnabled = true;
}

private async void sendPhoto_Clicked(object sender, EventArgs e)
{
	HttpContent fileStreamContent = new StreamContent(File.OpenRead(filePath));
	fileStreamContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { Name = "file", FileName = filename };
	fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");

	using (var client = new HttpClient())
	using (var formData = new MultipartFormDataContent())
	{
		formData.Add(fileStreamContent);
		var response = await client.PostAsync(url, formData);
	}
}

6. add `await CrossMedia.Current.Initialize();` in the android project in the mainactivity.cs just below the OnCreate call

7. add this code to the azure function:


string imageBase64;

using (var ms = new MemoryStream())
{
	file.CopyTo(ms);
	var fileBytes = ms.ToArray();
	imageBase64 = Convert.ToBase64String(fileBytes);
}

string sendgridApiKey = "api-key-here";

var client = new SendGridClient(sendgridApiKey);
var from = new EmailAddress("mymailaddress@partech.nl", "JP Hellemons");
var subject = "Declaraton from app";
var to = new EmailAddress("mymailaddress@partech.nl", "JP");

var msg = MailHelper.CreateSingleEmail(from, to, subject, "plain msg", "html version");
msg.AddAttachment(file.FileName, imageBase64, "image/jpeg", "attachment");
var response = await client.SendEmailAsync(msg);

return new OkObjectResult("");


8. of course make a free account at sendgrid and just follow the tutorial/docs for adding the nuget package to the Azure Function.


Next post will contain a link to the Azure Active Directory for authenticating so that not everybody will send in receipts on my behalf.

Good luck!

Post on LinkedIn
0 Comments

I followed this guide: https://medium.com/@tsjdevapps/use-fontawesome-in-a-xamarin-forms-app-2edf25311db4

Where you manually have to edit a XML file to get it to work in iOS and add the font for each platform you want to support. So for FontAwesome (regular, bold, brands) and UWP, Android, iOS. That gives 9 files!

which eventually works:

<Label Text="&#xf0f3;" FontFamily="{StaticResource FontAwesomeRegular}" FontSize="Large" TextColor="Red" />
         <Label Text="&#xf26e;" FontFamily="{StaticResource FontAwesomeSolid}" FontSize="Large" TextColor="Red" />
         <Label Text="&#xf26e;" FontFamily="{StaticResource FontAwesomeBrands}" FontSize="Large" TextColor="Red" />


BUT: If you would just update to Xamarin Forms 4.5.0.530 or higher

you can add a line for a font. (so 3 times something like this for FontAwesome)

[assembly: ExportFont("FontAwesome5Regular400.otf", Alias = "FontAwesome")]

to your app.xaml.cs

and use

<Label Text="&#xf0f3;" FontFamily="FontAwesome" FontSize="Large" TextColor="Green" />

in your mainpage.xaml


good luck!

Post on LinkedIn

To keep kids entertained, people from all over the world put a teddy bear in their window so that kids can spot them during a walk.

I don’t know the origin. It’s in Canada, USA, New Zealand, UK etc. It’s also in my hometown so I decided to make an app for it.

People made a Facebook group with a google form to submit the teddy bear and an URL with all bears on google maps.

I thought that I had to get access to the Google spreadsheet containing the data, but it seems that it would not hold that much info if we take a look at the entry form and the maps data. In Google maps you can download a KMZ file which is a zipped KML (Keyhole Markup Language) It’s XML. Here is the full KML:

full-kml

So there is a web link in it to get the live data. I used the webclient to pull it in and read it and tried to make pins out of it.

My first step was to add the Xamarin.Forms.Map Nuget package and the SharpKml.Core

Here is the full code:


private void AddMap()
{
	var map = new Map(MapSpan.FromCenterAndRadius(new Position(51.697815, 5.303675), Distance.FromMeters(10000)));

	using (var client = new WebClient())
	{
		var kmz = KmzFile.Open(client.OpenRead("https://www.google.com/maps/d/u/0/kml?mid=1kedGv2twtsWmzgxRpZcu5hr-qpE77plL"));
		Kml kml = kmz.GetDefaultKmlFile().Root as Kml;

		if (kml != null)
		{
			foreach (Placemark placemark in kml.Flatten().OfType())
			{
				Console.WriteLine(placemark.Name);

				var pin = new Pin()
				{
					Address = placemark.Address,
					Label = placemark.Name,
					Type = PinType.Place
				};
				map.Pins.Add(pin);
			}
		}
	}

	this.Content = map;
}

But the pins won’t show up (of course). Because I did not set the position. The real Kml contains data like this:

kml-detail

So as you can see there is no Latitude Longitude for the placemarks… So how does google maps work then? It seems that both Google Maps and Google Earth Geocode the address to get the lat lng. But that service unfortunately is not free. I tried to load the kml in Google Earth and export it, but that also does not add the latitude and longitude. I also thought that kml support for the map control could be used. So that I could just provide the kml to the map and have it sort it out, but that was build with monoandroid 9 instead of netstandard2.0 and would probably not fix the geocode issue.

I planned to make an app with no central backend. But because of geocoding, I would have to use a webapi or Azure Function to keep track of the “database” with all teddy bears and their corresponding lat lng. That would also lower the requests for geocoding if it would be moved from the phone (client) to the server.

But I’ve not found a good free geocoder

nominatim.openstreetmap.org does not work if I would provide the kml data. I think that I will come back to this one….


Good luck making your own KML/KMZ reading app!

Post on LinkedIn
0 Comments

This post covers storing and retrieving settings with a WPF combobox for the UI. If you are looking for a solution to store settings across devices, for instance a Windows Surface tablet and a Windows Phone powered device, you should check out the blog of Mike Taulty. That is called roaming data storage. And if you are not working on an app for Windows RT/metro or Windows Phone but are just coding some WPF application, you should read this post from Scott Hanselman.

Let’s start with some code now that it’s clear what this post covers and what it doesn’t cover.

Here is my XAML from my userinterface:

<ComboBox x:Name="cbCountry" 
    SelectionChanged="ComboBox_SelectionChanged" 
    PlaceholderText="Select country" SelectedValuePath="Tag">
    <ComboBoxItem Content="Australia" Tag="au"/>
    <ComboBoxItem Content="New Zealand" Tag="nz"/>
</ComboBox>

Please note the ‘SelectedValuePath’ is being set to ‘Tag’, otherwise it would grab the ‘comboboxitem’ or ‘content’ like this:

var comboboxContent = ((ComboBoxItem)cbCountry.SelectedValue).Content;

With the value path set to tag, it will grab ‘au’ or ‘nz’ as selected value.

This is the constructor of the xaml page containing my settings.

var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;

this.InitializeComponent();

if (localSettings.Values.ContainsKey("country"))
{
    cbCountry.SelectedValue = localSettings.Values["country"];
}

and this is the code for the selectionchanged event of the combobox.

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
    localSettings.Values["country"] = cbCountry.SelectedValue;
}

Good luck with this small code sample. If this is not what you are looking for, please check the links mentioned at the start. I strongly believe that they take you to the code that you are looking for (if the code above is insufficient)

Post on LinkedIn