Marcos Vainer Loeff

Getting started with Xamarin Android and PI Web API

Blog Post created by Marcos Vainer Loeff Employee on Dec 16, 2016

Introduction to Xamarin

 

Xamarin is a product that brings .NET/C# to both Android and iOS. Xamarin is pretty amazing in that it's fully .NET while being able to produce true Android and iOS apps at the same time, and apps that are compliant with the distribution requirements of both Google Play and the iOS App Store. On this blog post, we will develop a sample application developed using Xamarin Android that will retrieve the current value of sinusoid PI Point from a PI System on the cloud through PI Web API.

 

The sample application

 

In order to develop an Android application using Xamarin, you can use:

 

  • Xamarin Studio on a Mac
  • Visual Studio 2015 with Xamarin

 

Both options work great. In my case, I will be using Visual Studio.

 

If you prefer, the source code package is available on this GitHub repository.

 

Create a new project, select Visual C# --> Android --> Blank App.

 

 

Add a Portable Class Library for iOS, Android and Windows called PIWebAPIWrapper to your solution. This new project would be compatible with Android, iOS and Windows. Therefore, it is interesting to write all your business logic and models on this library specially if you plan to create an iOS version of your app. In this case, you will be able to integrate the Portable Class Library with your future iOS project.

 

 

Add the Microsoft.Net.Http NuGet package to your Portable Class library.

 

 

Repeat the same procedure for the Newtonsoft.Json NuGet package.

 

It is always a good practice to rebuild all your solution after adding packages to your Xamarin proejcts.

 

Let's start writing code to the Portable Class library first. Rename Class1 to PIWebApiService and add the following code to the file content:

 

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;


namespace PIWebAPIWrapper
{
    public class PIWebApiService
    {
        private string baseUrl = "https://webserver/piwebapi/";
        private string piDataArchiveName = "piservername";
        private string username = "username";
        private string password = "password";


        public async Task<string> DownloadWebData(string url)
        {
            var handler = new HttpClientHandler();
            handler.Credentials = new NetworkCredential(username, password);
            HttpClient httpClient = new HttpClient(handler);
            HttpResponseMessage httpMessage = await httpClient.GetAsync(url);
            if (httpMessage.IsSuccessStatusCode == true)
            {
                using (var stream = await httpMessage.Content.ReadAsStreamAsync())
                {
                    using (var streamReader = new StreamReader(stream))
                    {
                        return await streamReader.ReadToEndAsync();
                    }
                }
            }
            return string.Empty;
        }




        public async Task<double> GetSinusoidValue()
        {
            string url = string.Format(baseUrl + "points?path=\\\\{0}\\sinusoid", piDataArchiveName);
            string response = await DownloadWebData(url);
            JObject data = JsonConvert.DeserializeObject<dynamic>(response);
            string webId = data["WebId"].ToString();
            url = string.Format(baseUrl + "streams/{0}/value", webId);
            response = await DownloadWebData(url);
            JObject dataValue = JsonConvert.DeserializeObject<dynamic>(response);
            double value = Convert.ToDouble(dataValue["Value"].ToString());           
            return value;
        } 
    }
}

 

 

The PIWebApiService is responsable for getting the current value of the sinusoid PI Point by making two HTTP requests:

  1. Get the WebId of the sinusoid PI Point
  2. Get the current value of the sinusoid using the WebId returned on the previous step.

 

Let's move to the Android project. The main activity is the one that is going to appear once the application starts.

 

The layout of the MainActivity has mainly 3 objects:

 

  • TextView which will show the value of sinusoid
  • ProgressBar which will show a loading spinner while the app is making the HTTP requests
  • LinearLayout which defines the location of TextView and ProgressBar within the screen.

 

 

The code of the main.axml layout is below:

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:gravity="center"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/sinusoid_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

 

Finally, the code of the MainActivity is below:

 

using Android.App;
using Android.Widget;
using Android.OS;
using PIWebAPIWrapper;
using System.Threading.Tasks;


namespace XamarinPIWebAPIApp
{
    [Activity(Label = "PI Web API Sample Xamarin App", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        TextView sinusoidEditText = null;
        ProgressBar progressBar = null;
        Task t = null;


        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.Main);
            sinusoidEditText = FindViewById<TextView>(Resource.Id.sinusoid_text);
            progressBar = FindViewById<ProgressBar>(Resource.Id.progressBar);
            PIWebApiService service = new PIWebApiService();
            double value = -1;
            t  = Task.Run(async () =>
            {
                value = await service.GetSinusoidValue();
                this.RunOnUiThread(() =>
                {
                    progressBar.Visibility = Android.Views.ViewStates.Invisible;
                    sinusoidEditText.Text = string.Format("The current sinusoid value is {0}", value);
                });


            });
        }
    }
}

 

 

Here are the steps of what happens on the MainActivity:

 

  1. It renders the Main.axml layout.
  2. It finds the TextView and the ProgressBar and store them on private variables.
  3. It creates a new Task which will call:
    1. Service.GetSinusoidValue() to get the current value of the sinusoid.
    2. Once the value is retrieved, the progressBar needs to disappear and the value should appear on the text of the sinusoidEditText UI object.
    3. These lines of code will manipulate the UI. This is why it needs to be executed inside an Action which would be an input of the RunOnUiThread.

 

Conclusions

 

This blog post is about developing Android application using the native Xamarin Android. I think this is a really interesting option for us since our community is very familiar with C# and Android is one of the most popular mobile platforms in the market.

 

Please don't hesitate to share your comments below!

Outcomes