Using the Windows Phone Custom Contact Store

In previous versions of Windows Phone, you could always query the contact store to retrieve contact or calendar items. During that time I always wondered why I couldn’t just create my own contacts that can be shared with other applications and accessed through the People Hub. I guess more people had this problem and in Windows Phone 8 this has been addressed. Windows Phone 8 introduced the custom contact store in which apps can create contacts that are accessible from the People Hub and from other apps. Items in the custom contact store may only be modified by app that created them

How to create contacts in Windows Phone 8

In this section I would like to demonstrate how to use the custom contact store API. In order to do so we’ll create a UI that accepts the display name, email address, and mobile phone number. To make it a bit more fancy, we’ll add a feature that accepts a photo which can be loaded either from the camera or the media library

So here’s the code…

<phone:PhoneApplicationPage x:Class="CustomContactStore.MainPage"
                           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                           xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
                           xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
                           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                           mc:Ignorable="d"
                           FontFamily="{StaticResource PhoneFontFamilyNormal}"
                           FontSize="{StaticResource PhoneFontSizeNormal}"
                           Foreground="{StaticResource PhoneForegroundBrush}"
                           SupportedOrientations="Portrait"
                           Orientation="Portrait"
                           shell:SystemTray.IsVisible="True">

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="CUSTOM CONTACT STORE" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0" />
            <TextBlock Text="Sample" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" />
        </StackPanel>

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <TextBlock Text="Display Name" VerticalAlignment="Center" />
                    <TextBox Grid.Column="1" Name="displayName" />
                    <TextBlock Grid.Row="1" Text="Email" VerticalAlignment="Center" />
                    <TextBox Grid.Row="1" Grid.Column="1" Name="email" />
                    <TextBlock Grid.Row="2" VerticalAlignment="Center" Text="Mobile" />
                    <TextBox Grid.Row="2" Grid.Column="1" Name="mobile" />
                </Grid>
                <Button Content="Attach New Photo" Click="AttachNewPhotoClicked" />
                <Button Content="Attach Existing Photo" Click="AttachExistingPhotoClicked" />
                <Button Content="Save Contact" Click="AddClicked" />
            </StackPanel>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

Code Behind (C#)

using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Phone.Tasks;
using Windows.Phone.PersonalInformation;

namespace CustomContactStore
{
    public partial class MainPage
    {
        private Stream photo;

        public MainPage()
        {
            InitializeComponent();
        }

        private async void AddClicked(object sender, RoutedEventArgs e)
        {
            var store = await ContactStore.CreateOrOpenAsync();
            var contact = new StoredContact(store)
                              {
                                  DisplayName = displayName.Text
                              };

            var props = await contact.GetPropertiesAsync();
            props.Add(KnownContactProperties.Email, email.Text);
            props.Add(KnownContactProperties.MobileTelephone, mobile.Text);

            if (photo != null)
                await contact.SetDisplayPictureAsync(photo.AsInputStream());

            await contact.SaveAsync();

            if (photo != null)
                photo.Dispose();
        }

        private void AttachNewPhotoClicked(object sender, RoutedEventArgs e)
        {
            var task = new CameraCaptureTask();
            task.Completed += OnTaskOnCompleted;
            task.Show();
        }

        private void OnTaskOnCompleted(object o, PhotoResult result)
        {
            photo = result.ChosenPhoto;
        }

        private void AttachExistingPhotoClicked(object sender, RoutedEventArgs e)
        {
            var task = new PhotoChooserTask();
            task.Completed += OnTaskOnCompleted;
            task.Show();
        }
    }
}

To create a custom contact we need to use the ContactStore API, we create an instance of this using the helper method CreateOrOpenAsync(). Now that we have an instance of the contact store, we create an instance of a StoredContact and set the DisplayName property to the value of the display name entered in the UI. The StoredContact object is very limited but we can add KnownContactProperties such as Email and MobileTelephone. This is done by using the GetPropertiesAsync() method of the StoredContact instance. The photos can be attached using the CameraCaptureTask or the PhotoChooserTask. We attach the photos by calling the SetDisplayPictureAsync() method of the StoredContact instance. The API’s for the custom contact store are pretty straight forward and easy to use.

Manifest

The custom contact store requires the ID_CAP_CONTACTS capability, we should enable that in the WMAppManifest.xml file. In order to that, in the Visual Studio Solution Explorer, expand the project properties folder and double click the WMAppManifest.xml file. This will open the new UI editor for the manifest file. Go to the Capabilities tab and enable the ID_CAP_CONTACTS

[id_cap_contacts]

Once the manifest file has been updated the app should be able to launch.

The user interface looks like this:

[Custom Contact Store

Once the contact is created it will be available in the People Hub

[People Hub

When the contact is viewed from the People Hub the owner of the contact will be displayed on top

[Custom Contact

I hope you found this useful. You can check out the source code using the link below



The missing ResWFileCodeGenerator custom tool for Visual Studio 2012

I’ve been doing quite a lot of Windows Store Apps on Windows 8 lately. Ever since I started I always seemed to miss the ResXFileCodeGenerator custom tool. This is because in WinRT when you want to load a localized string in code then you will have to do something like this:

var resources = new ResourceLoader();
var localizedString = resources.GetString("SomeResourceName");

This of course can easily lead to a lot of errors. In my case I got a little to eager while refactoring and forgot to notice that I was also changing hard coded strings. This broke the code quite a lot. Because of this frustration I decided to implement my own custom tool since Microsoft didn’t provide one

The project is open source and is available at Github. Here’s a preview of the description which I took directly from my CodePlex project site.

Download from Visual Studio Marketplace

Features

  • Define custom namespace for the generated file
  • Auto-updating of generated code file when changes are made to the .ResW Resource file
  • XML documentation style comments like “Localized resource similar to ‘[the value]’”
  • Supports Visual Studio 2015, 2017, 2019, and 2022
  • Supports dotted keys - Replaces . with _ (e.g. Something.Awesome = Something_Awesome)

Custom Tools

  • ReswFileCodeGenerator - Generates a public class
  • InternalReswFileCodeGenerator - Generates an internal (C#) / friend (VB) class

Supported Languages

  • C#
  • Visual Basic

Screenshots

ReswFileCodeGenerator Custom Tool

InternalReswFileCodeGenerator Custom Tool

C# Usage

string test1, test2, test3;

void LoadLocalizedStrings()
{
    test1 = App1.LocalizedResources.Resources.Test1;
    test2 = App1.LocalizedResources.Resources.Test2;
    test3 = App1.LocalizedResources.Resources.Test3;
    test4 = App1.LocalizedResources.Resources.Test_With_Dotted_Keys;
}

Visual Basic Usage

Dim test1, test2, test3

Private Sub LoadLocalizedStrings()
    test1 = AppVb.LocalizedStrings.Resources.Test1
    test2 = AppVb.LocalizedStrings.Resources.Test2
    test3 = AppVb.LocalizedStrings.Resources.Test3
    test4 = AppVb.LocalizedStrings.Resources.Test_With_Dotted_Keys;
End Sub

Generated C# Code

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

// --------------------------------------------------------------------------------------------------
// <auto-generatedInfo>
// 	This code was generated by ResW File Code Generator (http://bit.ly/reswcodegen)
// 	ResW File Code Generator was written by Christian Resma Helle
// 	and is under GNU General Public License version 2 (GPLv2)
// 
// 	This code contains a helper class exposing property representations
// 	of the string resources defined in the specified .ResW file
// 
// 	Generated: 05/20/2019 15:47:37
// </auto-generatedInfo>
// --------------------------------------------------------------------------------------------------
namespace App2
{
    using Windows.ApplicationModel.Resources;


    public sealed partial class Resources
    {
    
        private static ResourceLoader resourceLoader;
    
        /// <summary>
        /// Get or set ResourceLoader implementation
        /// </summary>
        public static ResourceLoader Resource
        {
            get
            {
                if ((resourceLoader == null))
                {
                    Resources.Initialize();
                }
                return resourceLoader;
            }
            set
            {
                resourceLoader = value;
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test
        {
            get
            {
                return Resource.GetString("Test");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test2
        {
            get
            {
                return Resource.GetString("Test2");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test3
        {
            get
            {
                return Resource.GetString("Test3");
            }
        }
    
        /// <summary>
        /// Localized resource similar to "test"
        /// </summary>
        public static string Test_With_Dotted_Keys
        {
            get
            {
                return Resource.GetString("Test/With/Dotted/Keys");
            }
        }
    
        public static void Initialize()
        {
            string executingAssemblyName;
            executingAssemblyName = Windows.UI.Xaml.Application.Current.GetType().AssemblyQualifiedName;
            string[] executingAssemblySplit;
            executingAssemblySplit = executingAssemblyName.Split(',');
            executingAssemblyName = executingAssemblySplit[1];
            string currentAssemblyName;
            currentAssemblyName = typeof(Resources).AssemblyQualifiedName;
            string[] currentAssemblySplit;
            currentAssemblySplit = currentAssemblyName.Split(',');
            currentAssemblyName = currentAssemblySplit[1];
            if (executingAssemblyName.Equals(currentAssemblyName))
            {
                resourceLoader = ResourceLoader.GetForCurrentView("Resources");
            }
            else
            {
                resourceLoader = ResourceLoader.GetForCurrentView(currentAssemblyName + "/Resources");
            }
        }
    }
}

Generated Visual Basic Code

'------------------------------------------------------------------------------
' <auto-generated>
'     This code was generated by a tool.
'     Runtime Version:4.0.30319.42000
'
'     Changes to this file may cause incorrect behavior and will be lost if
'     the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------

Option Strict Off
Option Explicit On

Imports Windows.ApplicationModel.Resources

'--------------------------------------------------------------------------------------------------
'<auto-generatedInfo>
'	This code was generated by ResW File Code Generator (http://bit.ly/reswcodegen)
'	ResW File Code Generator was written by Christian Resma Helle
'	and is under GNU General Public License version 2 (GPLv2)
'
'	This code contains a helper class exposing property representations
'	of the string resources defined in the specified .ResW file
'
'	Generated: 05/20/2019 15:48:18
'</auto-generatedInfo>
'--------------------------------------------------------------------------------------------------

Partial Public NotInheritable Class Resources

    Private Shared resourceLoader As ResourceLoader

    '''<summary>
    '''Get or set ResourceLoader implementation
    '''</summary>
    Public Shared Property Resource() As ResourceLoader
        Get
            If (resourceLoader Is Nothing) Then
                Resources.Initialize
            End If
            Return resourceLoader
        End Get
        Set
            resourceLoader = value
        End Set
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test() As String
        Get
            Return Resource.GetString("Test")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test2() As String
        Get
            Return Resource.GetString("Test2")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test3() As String
        Get
            Return Resource.GetString("Test3")
        End Get
    End Property

    '''<summary>
    '''Localized resource similar to "test"
    '''</summary>
    Public Shared ReadOnly Property Test_With_Dotted_Keys() As String
        Get
            Return Resource.GetString("Test/With/Dotted/Keys")
        End Get
    End Property

    Public Shared Sub Initialize()
        Dim executingAssemblyName As String
        executingAssemblyName = Windows.UI.Xaml.Application.Current.GetType().AssemblyQualifiedName
        Dim executingAssemblySplit() As String
        executingAssemblySplit = executingAssemblyName.Split(Global.Microsoft.VisualBasic.ChrW(44))
        executingAssemblyName = executingAssemblySplit(1)
        Dim currentAssemblyName As String
        currentAssemblyName = GetType(Resources).AssemblyQualifiedName
        Dim currentAssemblySplit() As String
        currentAssemblySplit = currentAssemblyName.Split(Global.Microsoft.VisualBasic.ChrW(44))
        currentAssemblyName = currentAssemblySplit(1)
        If executingAssemblyName.Equals(currentAssemblyName) Then
            resourceLoader = ResourceLoader.GetForCurrentView("Resources")
        Else
            resourceLoader = ResourceLoader.GetForCurrentView(currentAssemblyName + "/Resources")
        End If
    End Sub
End Class



Parenthood

Once again I have taken a break from blogging. Parenting and my new family has been my absolute top priority and I’ve been having a hard time finding the time to write articles. I really enjoy spending time with my son and wife and I take advantage of every possible moment to do so.

I ended up at a schedule that starts around 5:30 AM in which my son wakes up, and wakes me up. I go and start preparing some breakfast for him. We then have a cozy morning playing around the house. I drop him off at day care as late possible (in my case 9:00 AM) and then drive to the office, or to a customer, or back home if I’m working from home. I pick my son up at between 3:00-4:00 PM and spend time with him and my wife until around 6:30 PM (this is his bed time and I’m very lucky that he has no trouble sleeping). From around 6:30 to 8:00-9:00 PM I spend some time with my wife discussing how our days went and do some planning for the following days. My wife goes to bed a bit early, and when that happens I go back to work. Since I drastically shortened by day, I need to do quite a bit of catching up at night. I usually work an extra 2-3 hours more at night, then I have an hour or 2 to give my mind a rest or work on a hobby project. My day usually ends between 12:30 AM and 2:00 AM. I need at least 4 hours of sleep to recharge for the next day, otherwise I won’t be able to last a full week on my schedule.

Hence the break from blogging :)



Windows Phone and HTML5 - Danish Developer Conference 2012 Session Recording

Here’s the recording of my session on Windows Phone and HTML5 for the Danish Developer Conference 2012, for those who missed it…

You can also watch it on YouTube



Accessing the Accelerometer from HTML5 and Javascript with Windows Phone 7

In my previous post I discussed how to have Javascript code hosted in a WebBrowser control execute .NET code in the host application and vice versa. I also demonstrated how to retrieve and display device status information using HTML5 hosted in a WebBrowser control.

For this sample I would like to demonstrate how to access the Accelerometer sensor from HTML5 and Javascript. To make things more interesting, the Accelerometer reading data will be constantly updated every 100 milliseconds and .NET code will repeatedly call a Javascript method as Accelerometer reading data gets updated

[

[

And here’s the code…

Default.html (HTML5 + Javascript)

The code below is going to be used as a local html file that is to be copied to isolated storage. Let’s put this in a folder called HTML

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=480, height=800, user-scalable=no" />
    <meta name="MobileOptimized" content="width" />
    <meta name="HandheldFriendly" content="true" />
    <title>HTML5 and Windows Phone 7</title>
    <style>
        body
        {
            color: White;
            background-color: Black;
            font-family: 'Segoe WP Semibold';
            text-align: left;
        }
        h3
        {
            font-size: 20pt;
        }
        input
        {
            color: #ffffff;
            background-color: #000000;
            border: 2px solid white;
            vertical-align: baseline;
            font-size: 17pt;
            min-width: 40px;
            min-height: 40px;
            margin: 5;
        }
    </style>
</head>
<body onload="onLoad()">
    <div>
        <h3>X:</h3>
        <input id="x" type="text" value="0" />
        <h3>Y:</h3>
        <input id="y" type="text" value="0" />
        <h3>Z:</h3>
        <input id="z" type="text" value="0" />
    </div>

    <script type="text/javascript">
        function onLoad() {
            window.external.notify("startAccelerometer");
        } 

        function accelerometerCallback(x, y, z) {
            document.getElementById("x").value = x;
            document.getElementById("y").value = y;
            document.getElementById("z").value = z;
        }
    </script>
</body>
</html>

MainPage.xaml

The code below is the main page of the Silverlight application that will host the HTML content

<phone:PhoneApplicationPage x:Class="PhoneApp.MainPage"
                           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                           xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
                           xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
                           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                           mc:Ignorable="d"
                           d:DesignWidth="480"
                           d:DesignHeight="768"
                           FontFamily="{StaticResource PhoneFontFamilyNormal}"
                           FontSize="{StaticResource PhoneFontSizeNormal}"
                           Foreground="{StaticResource PhoneForegroundBrush}"
                           SupportedOrientations="Portrait"
                           Orientation="Portrait"
                           shell:SystemTray.IsVisible="True"
                           Loaded="PhoneApplicationPage_Loaded">

    <Grid x:Name="LayoutRoot"
         Background="Transparent">
        <phone:WebBrowser Name="browser"
                         IsScriptEnabled="True"
                         Source="HTML/Default.html"
                         ScriptNotify="browser_ScriptNotify" />
    </Grid>

</phone:PhoneApplicationPage>

MainPage.xaml.cs

And here’s the code behind the xaml file

public partial class MainPage : PhoneApplicationPage
{
    private Microsoft.Devices.Sensors.Accelerometer accelerometer; 

    public MainPage()
    {
        InitializeComponent();
    } 

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
        using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (!store.DirectoryExists("HTML")) store.CreateDirectory("HTML");
            CopyToIsolatedStorage("HTML\\Default.html", store);
        }
    } 

    private static void CopyToIsolatedStorage(string file, IsolatedStorageFile store, bool overwrite = true)
    {
        if (store.FileExists(file) && !overwrite)
            return; 

        using (Stream resourceStream = Application.GetResourceStream(new Uri(file, UriKind.Relative)).Stream)
        using (IsolatedStorageFileStream fileStream = store.OpenFile(file, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            int bytesRead;
            var buffer = new byte[resourceStream.Length];
            while ((bytesRead = resourceStream.Read(buffer, 0, buffer.Length)) > 0)
                fileStream.Write(buffer, 0, bytesRead);
        }
    } 

    private void browser_ScriptNotify(object sender, NotifyEventArgs e)
    {
        if (e.Value == "startAccelerometer")
        {
            if (accelerometer == null)
            {
                accelerometer = new Microsoft.Devices.Sensors.Accelerometer { TimeBetweenUpdates = TimeSpan.FromMilliseconds(100) };
                accelerometer.CurrentValueChanged += (o, args) => Dispatcher.BeginInvoke(() =>
                {
                    var x = args.SensorReading.Acceleration.X.ToString("0.000");
                    var y = args.SensorReading.Acceleration.Y.ToString("0.000");
                    var z = args.SensorReading.Acceleration.Z.ToString("0.000"); 

                    browser.InvokeScript("eval", string.Format("accelerometerCallback({0},{1},{2})", x, y, z));
                });

                accelerometer.Start();
            }
        }
    }
}

What happens in the code above is that a Javascript method is executed that notifies the host application telling it to start the Accelerometer when the HTML has loaded. We then add an event handler to the Accelerometers CurrentValueChanged event that invokes the accelerometerCallback Javascript method and passing in Accelerometer reading data as the arguments. Notice that I use eval as the Javascript method to invoke and passing the method call as an argument, this is because the accelerometer reading data is retrieved on a worker thread and for some reason an unknown system error occurs even when executing code on the UI thread through the Page Dispatcher.BeginInvoke() method. I figured out that using eval was the only way to execute Javascript code from a .NET worker thread.

I hope you found this useful. You can grab the full source code for the example here: