Calendar Service for UWP and Raspberry Pi Windows Core

Below is some code that shows how to create a service for your Windows UWP apps to access your google calendars, and display them how you feel is best for your users.  This has been used sucessfully on a Raspberry Pi 3 board running windows Core to create a calendar for a kitchen touch screen display.

<p>Calendar Service for UWP and Raspberry Pi Windows Core</p>

First you need to add a reference to Google.Apis.Calendar.v3.  This can be found on Nuget and installed using 

Install-Package Google.Apis.Calendar.v3

Next you can use the following classes to create a service to access your calendar

CODE:

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BLAH
{
    public class CalendarService
    {
       private static string accessTokenKey = "{ACCESS_TOKEN}";
        private static string accessTokenValuePrefix = "access_token=";
        private static string accessJson = "access.json";
        private static string refreshJson = "refresh.json";
       
        dynamic authResp = null;
        dynamic authRespRefresh = null;

        public event DeviceRegisterHandler DeviceRegistrationRequested;
        public event ExceptionOccurredHandler ExceptionOccurred;
      
        public void OnExceptionOccurred(string exception, Exception innerException,  ExceptionLevel? exceptionLevel = ExceptionLevel.Information)
        {
            try
            {
                if (ExceptionOccurred != null)
                    ExceptionOccurred(new Exception(exception, innerException), exceptionLevel);
            }
            catch { }

        }
        public void OnDeviceRegistrationRequested( string deviceCode)
        {
            if (DeviceRegistrationRequested != null)
                DeviceRegistrationRequested(deviceCode);
        }
        public string ClientID { get; internal set; }
        public string ClientSecret { get; internal set; }
        public CalendarService(string clientID, string clientSecret)
        {
            ClientID = clientID;
            ClientSecret = clientSecret;
            accessJson = System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, accessJson);
            refreshJson = System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, refreshJson);
        }
        async Task<string> TryReadWR(System.Net.WebRequest req, string refreshToken)
        {
            System.Net.WebResponse resp = null;
            try
            {
                resp = await req.GetResponseAsync();
                var reader = new System.IO.StreamReader(resp.GetResponseStream());
                string rSting = reader.ReadToEnd();
                return rSting;
            }
            catch (System.Net.WebException ex)
            {
                if (((System.Net.HttpWebResponse)ex.Response).StatusCode == (System.Net.HttpStatusCode)401)
                {
                    if (refreshToken != "")
                    {
                        string reauth = await GetJSON("https://www.googleapis.com/oauth2/v3/token", "client_id=" + ClientID + "&client_secret=" + ClientSecret + "&refresh_token=" + refreshToken + "&grant_type=refresh_token", "", "application/x-www-form-urlencoded");
                        System.IO.File.WriteAllText(refreshJson, reauth);
                        string jRefresh = System.IO.File.ReadAllText(refreshJson);
                        authRespRefresh = Newtonsoft.Json.JsonConvert.DeserializeObject(jRefresh);
                        authResp.access_token = authRespRefresh.access_token;
                        return "";
                    }
                    else
                    {
                        throw ex;
                    }

                }
                else if (((System.Net.HttpWebResponse)ex.Response).StatusCode == (System.Net.HttpStatusCode)400)
                {
                    var exReader = new System.IO.StreamReader(((System.Net.HttpWebResponse)ex.Response).GetResponseStream());
                    string rString = exReader.ReadToEnd();
                    return rString;
                }
                else if (((System.Net.HttpWebResponse)ex.Response).StatusCode == (System.Net.HttpStatusCode)403)
                {
                    System.IO.File.Delete(accessJson);
                    File.Delete(refreshJson);
                    string jRegister = await GetJSON("https://accounts.google.com/o/oauth2/device/code", "client_id=" + ClientID + "&scope=https://www.googleapis.com/auth/calendar", "", "application/x-www-form-urlencoded");
                    dynamic tokeResp = Newtonsoft.Json.JsonConvert.DeserializeObject(jRegister);
                    string code = tokeResp.user_code;
                    OnDeviceRegistrationRequested(code);
                    return "";
                }

                else
                {
                    throw ex;
                }

            }


        }
        async Task<string> GetJSON(string URL, string body, string refreshToken, string contentType = "application/json")
        {
            string internalBody = body;
            string internalURL = URL;

            if (authResp != null)
            {
                internalBody = body.Replace(accessTokenKey, accessTokenValuePrefix + authResp.access_token);
                internalURL = URL.Replace(accessTokenKey, accessTokenValuePrefix + authResp.access_token);
            }
            var wr = System.Net.WebRequest.Create(internalURL);
            if (body != "")
            {
                wr.Method = "POST";
                wr.ContentType = contentType;
                var sw = new StreamWriter(await wr.GetRequestStreamAsync());
                sw.Write(internalBody);
                sw.Flush();
            }
            else
                wr.Method = "GET";


            string result = await TryReadWR(wr, refreshToken);
            if (result == "")
            {
                if (authResp != null)
                {
                    internalBody = body.Replace(accessTokenKey, accessTokenValuePrefix + authResp.access_token);
                    internalURL = URL.Replace(accessTokenKey, accessTokenValuePrefix + authResp.access_token);
                    wr = System.Net.WebRequest.Create(internalURL);
                }
                if (body != "")
                {
                    wr.Method = "POST";
                    wr.ContentType = contentType;
                    var sw = new System.IO.StreamWriter(await wr.GetRequestStreamAsync());
                    sw.Write(body);
                    sw.Flush();
                }
                else
                    wr.Method = "GET";
                return await TryReadWR(wr, refreshToken);
            }
            else
                return result;


        }
        public async Task Connect()
        {
            string jAuth = "";
            //Load Auth from Disk
            try
            {
                jAuth = System.IO.File.ReadAllText(accessJson);
                authResp = Newtonsoft.Json.JsonConvert.DeserializeObject(jAuth);
            }
            catch (Exception err)
            {
                OnExceptionOccurred("Could not read auth file" , err, ExceptionLevel.Warning);
            }
            if (System.IO.File.Exists(refreshJson))
            {
                try
                {
                    string jRefresh = System.IO.File.ReadAllText(refreshJson);
                    authRespRefresh = Newtonsoft.Json.JsonConvert.DeserializeObject(jRefresh);
                    authResp.access_token = authRespRefresh.access_token;
                }
                catch (Exception err)
                {
                    OnExceptionOccurred("Could not read refresh file", err, ExceptionLevel.Warning);
                    
                }
            }
            dynamic tokeResp = null;
            //if This machine has never authenticated
            if (authResp == null)
            {
                string jRegister = await GetJSON("https://accounts.google.com/o/oauth2/device/code", "client_id=" + ClientID + "&scope=https://www.googleapis.com/auth/calendar", "", "application/x-www-form-urlencoded");
                tokeResp = Newtonsoft.Json.JsonConvert.DeserializeObject(jRegister);
                string code = tokeResp.user_code;
                OnDeviceRegistrationRequested(code);
            }
            //wait for device activation
            while (authResp == null)
            {
                await Task.Delay(((int)tokeResp.interval * 1000));
                object oCode = tokeResp.device_code;
                try
                {
                    jAuth = await GetJSON("https://www.googleapis.com/oauth2/v3/token", "client_id=" + ClientID + "&client_secret=" + ClientSecret + "&code=" + oCode.ToString() + "&grant_type=http://oauth.net/grant_type/device/1.0", "", "application/x-www-form-urlencoded");
                    dynamic tempAuthResp = Newtonsoft.Json.JsonConvert.DeserializeObject(jAuth);
                    if (tempAuthResp.error == "authorization_pending")
                        System.Diagnostics.Debug.WriteLine("Authorization Pending");
                    else if (tempAuthResp.error == "slow_down")
                        System.Diagnostics.Debug.WriteLine("Slow Down");
                    else
                    {
                        System.Diagnostics.Debug.WriteLine("Auth Token Received:\r\n" + jAuth);
                        authResp = tempAuthResp;
                        System.IO.File.WriteAllText(accessJson, jAuth);
                        System.Diagnostics.Debug.WriteLine("Auth Saved");
                    }
                }
                catch (Exception err)
                {
                    System.Diagnostics.Debug.WriteLine("Could not authenticate: " + err.Message);
                }

            }
        }
        public async Task<string> GetEvents(string calendar = "primary")
        {
            string data = "";
            if (!calendar.Contains("#"))
                data = await GetJSON("https://www.googleapis.com/calendar/v3/calendars/" + System.Net.WebUtility.UrlEncode(calendar) + "/events?" + accessTokenKey + "&singleEvents=true", "", authResp.refresh_token.ToString());
            else
            
                {
                    string uri = "https://www.googleapis.com/calendar/v3/calendars/" + System.Net.WebUtility.UrlEncode(calendar) + "/events?key=<KEY>";
                    data = await GetJSON(uri, "", "");
                }
            return data;
        }
        public async Task<string> GetEvents(DateTime MinTime, string calendar = "primary")
        {
            string data = await GetJSON("https://www.googleapis.com/calendar/v3/calendars/" + calendar + "/events?" + accessTokenKey + "&singleEvents=true&timeMin=" + System.Xml.XmlConvert.ToString( MinTime.Date, System.Xml.XmlDateTimeSerializationMode.Utc), "", authResp.refresh_token.ToString());
            return data;
        }
        public async Task<string> GetEvent(string eventID, string calendar = "primary")
        {
            string data = await GetJSON("https://www.googleapis.com/calendar/v3/calendars/" + calendar + "/events/" + eventID + "?" + accessTokenKey + "&singleEvents=true", "", authResp.refresh_token.ToString());
            return data;
        }
        public async Task<string> InsertEvent(Data.Event eventItem, string calendar = "primary")
        {
            string jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(eventItem);
            return await InsertEvent(jsonData, calendar);
        }
        public async Task<string> InsertEvent(string eventJSON, string calendar = "primary")
        {
            string data = await GetJSON("https://www.googleapis.com/calendar/v3/calendars/" + calendar + "/events?" + accessTokenKey , eventJSON, authResp.refresh_token.ToString());
            return data;
        }
    }
    public enum ExceptionLevel { Critical, Warning, Information }
    public delegate void DeviceRegisterHandler(string deviceCode);
    public delegate void ExceptionOccurredHandler(Exception exception, ExceptionLevel? exceptionLevel);
        
}

Once you have created the service you need one supporting class to deal with the event objects.  This class provides you access to the google calendar raw data in a nice C# view, parsed to allow your UWP app to display the calendar event as you like.

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Blah.Data
{
    public class TimePeriod
    {
        public TimePeriod()
        {
            timeZone = "America/New_York";
        }
        public DateTime dateTime { get; set; }
        public string timeZone { get; set; }

    }

    

   

    public class Event
    {
        public Event()
        {
            end = new TimePeriod();
            start = new TimePeriod();
        }
        public TimePeriod end { get; set; }
        public TimePeriod start { get; set; }
        public string description { get; set; }
        public string summary { get; set; }
    }
}


 

You are now all set and this can be added to any of your UWP apps to allow data from Google Calendar to be shown.  The pattern used here is actually also useful if you are connecting to other OAuth 2.0 web services that require retries and reconnections.

 

Enjoy and happy coding!

 

Last Updated: 12/15/2016 10:28:38 PM