(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Windows 8.1: Improved device interactions

(3 votes)
Andrea Boschin
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
0 comments   /   posted on Dec 02, 2013
Categories:   Windows 8


When developing for a traditional computer, we are use to almost completely forget the environment where we are running. Usually each application works without being aware of other software installed and of device services like an address book or a calendar. This is different in Windows 8, when you run inside the Windows Runtime, where you can share between applications using contracts and you can rely on some baseline services that provide a uniform usability to the user.  In Windows 8.1 you gain some new features in this area, that let you being much more in contact with the underlying operating system and provide a better user experience.

Appointments and Contacts

In Windows 8 the interaction with the calendar and address book of the device was really basic. It only allowed to add, update and remove contacts from the address book. In the new version of the operating system it is now also possible to manage appointments as well as the contacts, but you are also able to write a replacement application for the default calendar application. This not only means you can manage appointments invoking the system application, but also you can completely rewrite the system application to manage appointments and being invoked by other apps for appointment actions. Let examine the following snippet of code:

   1: private string id;
   3: private async void AddAppointment_Click(object sender, RoutedEventArgs e)
   4: {
   5:     Appointment appointment = new Appointment();
   7:     appointment.Subject = "Going to cut my hairs";
   8:     appointment.Location = "Venice";
   9:     appointment.StartTime = new DateTimeOffset(2013, 12, 5, 17, 0, 0, TimeSpan.FromHours(1));
  10:     appointment.Duration = TimeSpan.FromHours(1);
  12:     var buttonRect = this.GetElementRect((FrameworkElement)sender);
  13:     this.id = await AppointmentManager.ShowAddAppointmentAsync(appointment, buttonRect, Windows.UI.Popups.Placement.Below);
  15:     if (!string.IsNullOrEmpty(id))
  16:     {
  17:         MessageDialog dialog = new MessageDialog(this.id);
  18:         await dialog.ShowAsync();
  19:     }
  20: }

With this simple code we are able to invoke the default calendar application to ask to add a new appointment. The appointment we are about to add is very simple, made only of a subject, a location, a start time and a duration. These are only some of a huge number of properties we are able to set when we add an appointment.


The other properties also include the Details, BusyStatus, Reminder, Sensitivity, an associated Url, and also details about organizer and invitees and invitations. These are the common properties you are use to set when you create an appointment in a modern calendar application like outlook.  With this code, the system is invoked and it will open a popup where it is shown the ui provided by the default application. The image on the right side shows the resulting popup.

As a result of the operation you can get an empty string meaning that the popup has been dismissed without adding any appointments or the unique identifier of the appointment.

With this identifier you are able to invoke replacement or deletion of existing appointment. These operations also show a popup that ask the confirmation of the operation, with details about the appointment that is about to be replaced or deleted. When you replace an appointment the identifier of the new appointment is the same as the previous appointment but always rely to the returned id because the behavior may be different when the default application is replaced by a custom application.

Finally you can also invoke the calendar to show a specific timeframe, to enable your user to scan for free slots to assign to new appointmente. In this case the UI will not be invoked in a popup but will be opened on an half of the screen. Here is a snippet of code to show how to invoke these other operations.

   1: private async void RemoveAppointment_Click(object sender, RoutedEventArgs e)
   2: {
   3:     var buttonRect = this.GetElementRect((FrameworkElement)sender);
   4:     await AppointmentManager.ShowRemoveAppointmentAsync(this.id, buttonRect);
   5: }
   7: private async void ReplaceAppointment_Click(object sender, RoutedEventArgs e)
   8: {
   9:     Appointment appointment = new Appointment();
  11:     appointment.Subject = "Going to cut my hairs AGAIN!";
  12:     appointment.Location = "Venice";
  13:     appointment.StartTime = new DateTimeOffset(2013, 12, 5, 18, 0, 0, TimeSpan.FromHours(1));
  14:     appointment.Duration = TimeSpan.FromHours(1);
  16:     var buttonRect = this.GetElementRect((FrameworkElement)sender);
  17:     this.id = await AppointmentManager.ShowReplaceAppointmentAsync(this.id, appointment, buttonRect);
  19:     if (!string.IsNullOrEmpty(id))
  20:     {
  21:         MessageDialog dialog = new MessageDialog(this.id);
  22:         await dialog.ShowAsync();
  23:     }
  24: }
  25: private async void ShowTimeframe_Click(object sender, RoutedEventArgs e)
  26: {
  27:     await AppointmentManager.ShowTimeFrameAsync(new DateTimeOffset(2013, 12, 5, 18, 0, 0, TimeSpan.FromHours(1)), TimeSpan.FromHours(1));
  28: }

As I've anticipated, you can also provide your own calendar application and being active in the other side of the calls I've described in the previous paragraphs. This means you can wrap up your own company calendar with a custom application and make it automatically handled by the device and invoked by each application that need to add/remove appointments. For this purpose you have to add the following code to your application's manifest:

   1: <Extensions>
   2:   <m2:Extension Category="windows.appointmentsProvider">
   3:     <m2:AppointmentsProvider>
   4:       <m2:AppointmentsProviderLaunchActions>
   5:         <m2:LaunchAction Verb="addAppointment" EntryPoint="XPG.DeviceInteraction.App"/>
   6:         <m2:LaunchAction Verb="replaceAppointment" EntryPoint="XPG.DeviceInteraction.App"/>
   7:         <m2:LaunchAction Verb="removeAppointment" EntryPoint="XPG.DeviceInteraction.App"/>
   8:         <m2:LaunchAction Verb="showTimeFrame" EntryPoint="XPG.DeviceInteraction.App"/>
   9:       </m2:AppointmentsProviderLaunchActions>
  10:     </m2:AppointmentsProvider>
  11:   </m2:Extension>
  12: </Extensions>

This block should be inserted into the Application tag, be only sure you have defined the "m2" namespace as the following uri: http://schemas.microsoft.com/appx/2013/manifest. Each LaunchAction is related to the verb you want to handle and will cause your application to be activated with the ActivationKind equals to AppointmentsProvider. The area of the popup will be an application view that hosts a completely new instance of your application.

Managing appointments also means the need to query contacts - as an example to choose invitees for an event - but this is already supported by the previous Windows 8 api. In this release they add a new feature that lets you answer to contact actions. Contact actions are the one shown when you search a contact and you are presented with a number of buttons related to the information of the contact. A simple action could be the call to its phone number, the mapping of its address, the composition of a mail message when the contact exposes a mail address, etc...

Again to enable this feature you have to manually change the package manifest:

   1: <Extensions>
   2:     <Extension Category="windows.protocol">
   3:       <Protocol Name="tel"/>
   4:     </Extension>  
   5:     <m2:Extension Category="windows.contact">
   6:       <m2:Contact>
   7:         <m2:ContactLaunchActions>
   8:           <m2:LaunchAction Verb="call">
   9:             <m2:ServiceId>telephone</m2:ServiceId>
  10:           </m2:LaunchAction>
  11:           <m2:LaunchAction Verb="message">
  12:             <m2:ServiceId>skype.com</m2:ServiceId>
  13:           </m2:LaunchAction>
  14:           <m2:LaunchAction Verb="map"/>
  15:         </m2:ContactLaunchActions>
  16:       </m2:Contact>
  17:     </m2:Extension>  
  18: </Extensions>

The declaration for the "tel" protocol will handle the call to PSTN numbers but the additional extensions will handle the activation for other type of actions like skype calls, messages and mapping. As for the appointments activation these should be handled in the OnLaunch and OnActivated events of the application where you will receive an ActivationKind of type Contact.

   1: protected override void OnActivated(IActivatedEventArgs args)
   2: {
   3:     if (args.Kind == ActivationKind.Contact)
   4:     {
   5:         var contactArgs = args as IContactActivatedEventArgs;
   7:         if (contactArgs.Verb == Windows.ApplicationModel.Contacts.ContactLaunchActionVerbs.Map)
   8:         {
   9:             IContactMapActivatedEventArgs mapArgs = contactArgs as IContactMapActivatedEventArgs;
  10:             this.ActivateForContact(mapArgs.Contact, mapArgs.Address);
  11:         }
  12:     }
  13: }
  15: private void ActivateForContact(Contact contact, ContactAddress contactAddress)
  16: {
  17:     // TODO: Activate here the required page
  18: }

Interact with libraries

Another interesting feature, that should give much more value to your apps is the ability of manage libraries. A library is a folder that contains items of the same type. They can be Pictures, Video, Music and Documents. With this new feature, you application can ask to add a folder to one of these libraries and be able to manage files inside of it. The first thing to do is to take a reference to the library you need to work with:

   1: StorageLibrary library = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Documents);

Once you get a reference to the library you are able to add and remove a folder. this shows the system picker that let you choose the existing folder, also if it is hosted in skydrive. The folder then, is completely under your control and you can create and remove files. Here is a snippet that show a complete activity:

   1: private async void StorageLibraryAdd_Click(object sender, RoutedEventArgs e)
   2: {
   3:     try
   4:     {
   5:         StorageLibrary library = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Documents);
   6:         StorageFolder folder = await library.RequestAddFolderAsync();
   8:         // check if user canceled
   9:         if (folder != null)    
  10:         {
  11:             StorageFile file = await folder.CreateFileAsync("document.txt");
  13:             using (StreamWriter writer = new StreamWriter(await file.OpenStreamForWriteAsync()))
  14:             {
  15:                 writer.WriteLine("this is a text document...");
  16:             }
  17:         }
  18:     }
  19:     catch(Exception ex)
  20:     {
  21:         MessageDialog dialog = new MessageDialog(ex.Message);
  22:         dialog.ShowAsync();
  23:     }
  24: }

Be aware that wher you call the RequestAddFolderAsync method, it can return a null value meaning the user canceled the addition.

To enable the access to the libraries you ar requested to add a capability to the manifest, from the editor. Remember that, if you need to access the Documents library you also have to add a file association to associate the files you create with your application .

   1: <!-- file association -->
   3: <Extension Category="windows.fileTypeAssociation">
   4:   <FileTypeAssociation Name="txt">
   5:     <DisplayName>Text</DisplayName>
   6:     <EditFlags OpenIsSafe="true" />
   7:     <SupportedFileTypes>
   8:       <FileType ContentType="text/plain">.txt</FileType>
   9:     </SupportedFileTypes>
  10:   </FileTypeAssociation>
  11: </Extension>
  13: <!-- capabilities -->
  15: <Capabilities>
  16:   <Capability Name="internetClient" />
  17:   <Capability Name="picturesLibrary" />
  18:   <Capability Name="musicLibrary" />
  19:   <Capability Name="videosLibrary" />
  20:   <Capability Name="documentsLibrary" />
  21: </Capabilities>

Manage the touch keyboard

When you are on a touch device, without a mouse, the operating system tries to automatically popup the touch keyboard when you enter a textbox, because this means you need to type some characters. Until today only the text input elements are enabled to cause the touch keyboard to be showed, but there are cases when you may need to enter some text, also if you have not any textbox. For Windows 8.1 this is now possible using the automation helpers that associate to an arbitray item the requirements to have the touch keyboard opened.

Given you created a control, also an user control, the first thing is to create an automation peer. Implementing an automation peer is not a very simple thing. It is a class that implements a number of interfaces - in this case it implement ITextProvider and IValueProvider together with the FrameworkElementAutomationPeer base class - to provide services related to the behavior of the interface in the environment. Once the provider has been created - with all its logic and eventually nested classes - you must override the OnCreateAutomationPeer method in the control and return an instance of the automation peer:

   1: protected override Windows.UI.Xaml.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
   2: {
   3:     return new CustomTextInputAutomationPeer(this);
   4: }

After this has been accomplished (I'll attach a project containig a base automation peer created using MSDN samples as a track), you must ensure the focus is set to your control. This means hook the OnTapped event and set the focus to the control. Once the focus had been set, the automation peer makes the work causing the touch keyboard to be showed up. Here is the full code of the user control:

   1: public sealed partial class CustomTextInput : UserControl
   2: {
   3:     public CustomTextInput()
   4:     {
   5:         this.InitializeComponent();
   6:         this.IsTapEnabled = true;
   7:         this.IsTabStop = true;
   8:         this.Text = string.Empty;
   9:     }
  11:     protected override Windows.UI.Xaml.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
  12:     {
  13:         return new CustomTextInputAutomationPeer(this);
  14:     }
  16:     protected override void OnTapped(TappedRoutedEventArgs e)
  17:     {
  18:         this.Focus(Windows.UI.Xaml.FocusState.Pointer);
  19:     }
  21:     public string Text { get; set; }
  22: }

Obviously you can then get touch inputs using KeyDown event in the control. Beautiful feature, but I would like something much more simple and straightforward...


In this article I talked about some new features that may sounds trivial, but as an careful exam they are really powerful to give a better feedback to your user. Besides the touch keyboard that requires a lot of work to implement a complex automation peer, alle of them are easy to be implemented and should be taken in serious consideration every time you need to develop a new app.



No comments

Add Comment

Login to comment:
  *      *       

From this series