Compartilhar via


So you want to write a calendar replacement app? A look into Appointments Providers in Windows 8.1

First off, writing a calendar replacement app is no easy feat and this post is not designed to instruct you in how to manage all the terrible evils of calendaring when building your calendar app, it's about how to use the new Appointments Provider activation kind in Windows 8.1 to enable your app to accept and manage appointments created by other apps.

In Windows 8.1 we've enabled the ability for any Windows Store App to create appointments and add them to the user's calendar, update that appointment by replacing it on the user's calendar and the ability to remove those appointments that they've created through the AppointmentManager and AppointmentsProvider APIs.

Appointments Providers are apps that declare to be capable of handling requests from other apps to perform this addition, replacement and removal of appointments for the user and any calendar app can and should implement this functionally. For this overview of the Appointments Provider side of the equation, I'm going to start with the Appointments API sample available on MSDN and turn it into an appointments provider app to demonstrate how to declare yourself as a provider and how to handle being activated by the AppointmentManager APIs.

 

What you're going to need to get started:

  1. You're going to need Windows 8.1 and Visual Studio 2013.
  2. Familiarize yourself with Activation in Windows 8. (JavaScript, C#/C++).
  3. Download the Appointments API sample in your favorite language from msdn: https://code.msdn.microsoft.com/windowsapps/Appointments-API-sample-2b55c76e (I'm going to be using C# in this post if you intend on just copying and pasting your way through the tutorial).
  4. Take a look at the AppointmentManager APIs which are used by Windows Store apps to launch an Appointments Provider (your calendar app) to manage their Appointments.
  5. Check out the Appointment Properties that your calendar app should support as a good Windows citizen.
  6. Read over the AppointmentsProvider namespace since these are the primary types you'll be dealing with when implementing your appointments provider functionality.

 

Step 1: Registering your app as an Appointments Provider

In order to let Windows know that your app is capable of managing Appointments you need to update your app's manifest to include the AppointmentsProvider extension. You may have noticed though that it's not as straight forward as the existing "Declarations" when viewing the package.appxmanifest for your project. Being an Appointments Provider takes a lot of responsibility because an end user can only have one registered at a time so this is a bit of a hidden extension to keep it from being something every app feels like they should implement. The way to do go about it is to manually edit your manifest file as so:

1. Right click on "Package.appxmanifest" in your solution and select "View Code"

2. Make sure your manifest has the Windows 8.1 schema declared:

<Package xmlns="https://schemas.microsoft.com/appx/2010/manifest"
xmlns:m2="https://schemas.microsoft.com/appx/2013/manifest">

3. Under the Application tag, register the AppointmentsProvider extension.

<Extensions>
  <m2:Extension Category="windows.appointmentsProvider">
    <m2:AppointmentsProvider>
      <m2:AppointmentsProviderLaunchActions>
        <m2:LaunchAction Verb="addAppointment" Executable="Appointments.exe" EntryPoint="Appointments.App"/>
        <m2:LaunchAction Verb="replaceAppointment" Executable="Appointments.exe" EntryPoint="Appointments.App"/>
        <m2:LaunchAction Verb="removeAppointment" Executable="Appointments.exe" EntryPoint="Appointments.App"/>
        <m2:LaunchAction Verb="showTimeFrame" Executable="Appointments.exe" EntryPoint="Appointments.App"/>
      </m2:AppointmentsProviderLaunchActions>
    </m2:AppointmentsProvider>
  </m2:Extension>
</Extensions>

NOTE: In JavaScript/HTML5 apps you'll need to provide a StartPage rather than an entry point, e.g. <m2:LaunchAction Verb="removeAppointment" StartPage="removeAppointment.html" />

At this point we've told the system that our app is an appointments provider and if you build and run your project you should notice in the PC Settings app under "Search and apps" -> "Defaults" your app should be an option under "Calendar"

 

Step 2: Handling the AppointmentsProvider activation kind

Being an appointments provider means that your app must handle each of the four appointments provider "Verbs" which stand for the various actions an appointments provider can perform for other apps, those include Add, Remove and Replace an Appointment and ShowTimeFrame, which is basically just activating the appointments provider app to show the user's calendar at a particular date and time. You can determine which verb you were activated for by querying for IAppointmentsProviderActivatedEventArgs and inspecting the Verb property. You can use the AppointmentsProviderLaunchActionVerbs statics class to help in the comparison.

Here's an example of the OnActivated handler:

protected override void OnActivated(IActivatedEventArgs args)
{
    if (args.Kind == ActivationKind.AppointmentsProvider)
    {
        IAppointmentsProviderActivatedEventArgs providerArgs = args as IAppointmentsProviderActivatedEventArgs;
        if (providerArgs.Verb.Equals(AppointmentsProviderLaunchActionVerbs.AddAppointment))
        {

        }
        else if (providerArgs.Verb.Equals(AppointmentsProviderLaunchActionVerbs.RemoveAppointment))
        {

        }
        else if (providerArgs.Verb.Equals(AppointmentsProviderLaunchActionVerbs.ReplaceAppointment))
        {

        }
        else if (providerArgs.Verb.Equals(AppointmentsProviderLaunchActionVerbs.ShowTimeFrame))
        {

        }
    }
}

Now if you set a break point on the OnActivated function and run the sample, you can test the various scenarios and watch your app get activated for each verb. Here's a screenshot of a new instance of the sample app being activated for the Add Appointment verb at the splash screen:

TIP: I strongly recommend using the VS Simulator when debugging Appointments Providers considering the Appointments Provider is launched in a hosted flyout style window. The hosted window is dismissed any time the user clicks or touches outside of the window.

 

Step 3: Dealing with appointment operations

Now you're app is registered properly and being activated for each of the appointments provider launch action verbs. This is where your part comes to play, you have to add the logic to add, remove, or replace the appointment that the other app wants and report status back. The add, remove and replace arguments each have an Operation member that your app will use to manage the lifetime of that appointment operation. The basic flow is this:

  • Once you know which verb you were activated for, cast the IAppointmentsProviderActivatedEventArgs to the appropriate type: AppointmentsProviderAddAppointmentActivatedEventArgs, AppointmentsProviderRemoveAppointmentActivatedEventArgs, AppointmentsProviderReplaceAppointmentActivatedEventArgs, or AppointmentsProviderShowTimeFrameActivatedEventArgs.
  • Update your UI with the appropriate information based on the information given to you on the event args. For example, you may want to show the appointment information with a confirmation button for Add Appointment using the AddAppointmentOperation.AppointmentInformation member.
  • Once the user clicks your "Okay" button to confirm the add, remove or replace then you want to dismiss the flyout your app is hosted in so that you can process the request without the user having to wait on it. Each operation type has this DismissUI method that will hide your flyout and indicate to Windows that you're finished with user interaction.
  • When you've finished actually doing the work in the background to save or remove the appointment then you should call the appropriate ReportCompleted method on the operation member.

Add Appointment Example

AppointmentsProviderAddAppointmentActivatedEventArgs addArgs = args as AppointmentsProviderAddAppointmentActivatedEventArgs;
var appointmentToAdd = addArgs.AddAppointmentOperation.AppointmentInformation;

// Show UI with this appointment information populated, maybe allow the user to edit some of the properties before saving.

// When the user clicks "Okay" call DismissUI to tell Windows you're done with user interaction
addArgs.AddAppointmentOperation.DismissUI();

// Do work in the background to save the results and then call report completed with a generated globaly unique appointment id
addArgs.AddAppointmentOperation.ReportCompleted("Appointment Id");

Remove Appointment Example

AppointmentsProviderRemoveAppointmentActivatedEventArgs removeArgs = args as AppointmentsProviderRemoveAppointmentActivatedEventArgs;
var appointmentIdOfAppointmentToRemove = removeArgs.RemoveAppointmentOperation.AppointmentId;

// Look up the appointment and determine whether or not to remove the entire series or just an instance
Appointment appointmentToRemove = GetAppointmentFromDatabase(appointmentIdOfAppointmentToRemove);
if (appointmentToRemove.Recurrence != null)
{
    // If this appointment has recurrence, the instanceStartDate property on RemoveAppointmentOperation tells you which instance to remove
    DateTimeOffset instanceDateToRemove = removeArgs.RemoveAppointmentOperation.InstanceStartDate.Value;
}

// Populate UI and ask the user to confirm the removal

// When the user clicks "Okay" call DismissUI to tell Windows you're done with user interaction
removeArgs.RemoveAppointmentOperation.DismissUI();

// When you've finished removing the appointment from your app data, call ReportCompleted to tell the calling app that it was removed
// If the appointment couldn't be found then you should still call ReportCompleted
removeArgs.RemoveAppointmentOperation.ReportCompleted();

 

NOTE: The ShowTimeFrame verb activates you in a standard main window split 50/50 along with the calling app. There are no special lifetime considerations for ShowTimeFrame like there are for Add, Replace and Remove since you're not being activated in a hosted flyout. Just take the TimeToShow and Duration properties on the AppointmentsProviderShowTimeFrameActivatedEventArgs and display a nice view of the user's calendar around that time.

TIP: The flyout dimensions are fixed at 320px by 400px so be very careful when crafting your UI for the Add, Remove and Replace operations.

 

I could go into a lot more details about how to tailor your app to become an amazing calendar replacement through the use of the new Appointments Provider APIs in Windows 8.1 but think that this quick tutorial should give you enough to get started. Please leave any specific questions you may have in the comments section and I'll be happy to address them. Happy coding!

 

-Nick