Loupe

Intégration d'une application UWP à Cortana (1 sur 2)


Cortana

Depuis W10 et l'intégration forte de Cortana à Windows, le système de commande vocale des applications a évolué.
En effet, il est désormais possible de communiquer avec son application sans que celle-ci soit démarrée.
Pour cela, nous retrouvons toujours le mécanisme de commande vocale, mais avec cette fois la possibilité depuis Cortana de contacter notre application sans la lancer et à celle-ci de répondre de manière vocale et visuelle via l'interface de Cortana.

Nous allons coder ensemble un exemple d'application qui s'intègre à Cortana pour mieux comprendre tout cela.

Création de grammaires dans une app UWP

Dans un premier temps il faut créer une app UWP classique. Une blank app suffira car elle n'est pas vouée à être affichée et son IHM nous importe donc assez peu.
BlankApp

 

Création du fichier de grammaires

Depuis Windows Phone nous avons presque toujours eu la possibilité de démarrer nos applications à la voix.
Cela permet de ne pas avoir à chercher son application mais surtout de lancer son application dans un contexte prédéfini, sur une page de catégorie particulière par exemple.
Cette fonctionnalité était disponible en intégrant des fichiers VCD qui permettaient à l'application de déclarer les "grammaires", sortes d'expressions régulières de phrases, qu'elle reconnaît et le comportement associé.

 

Ajoutez un fichier VoiceCommands.xml

 

Une partie importante du fichier est l'élément CommandSet et la langue associée :

<CommandSet xml:lang="fr-fr" Name="CortanaSampleAppCommandSet_fr-fr">

Il sera nécessaire de l'adapter selon la langue de votre Cortana car c'est cette langue configurée qui rendra vos commandes utilisables ou pas.

Voyons ensuite la déclaration d'une commande.

<Command Name="turnOnLight">
        <Example> Allume la lumière du salon </Example>
        <ListenFor RequireAppName="BeforePhrase"> allume [moi] la lumière [de] [la] [du] {room}</ListenFor>
        <Feedback> J'allume la lumière {room}</Feedback>
        <VoiceCommandService Target="CortanaSampleService"/>
</Command>

Comme vous pouvez le voir certains mots sont optionnels et encadrés de crochets.
D'autres en revanche sont dynamiques et sont encadrés entre accolades. Ces éléments dynamiques, faisant partie d'une liste, doivent être déclarés sous la forme suivante :

<PhraseList Label="room">
        <Item>Salon</Item>
        <Item>Cuisine</Item>
        <Item>Chambre</Item>
</PhraseList>


La plupart du temps ces éléments correspondront à des catégories statiques de votre app mais dans certains cas il faudra modifier dynamiquement le fichier vcd avant son inscription aux commandes vocales.

Voici à quoi doit donc ressembler votre fichier vcd:

<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
  <CommandSet xml:lang="fr-fr" Name="CortanaSampleAppCommandSet_fr-fr">

      <AppName>Jarvis</AppName>
      <Example>Jarvis, allume la lumière</Example>

      <Command Name="showLightsList">
        <Example> Allume la lumière</Example>
        <ListenFor RequireAppName="BeforePhrase"> allume la lumière</ListenFor>
        <Feedback> J'affiche la liste </Feedback>
        <VoiceCommandService Target="CortanaSampleService"/>
      </Command>

      <Command Name="turnOnLight">
        <Example> Allume la lumière du salon </Example>
        <ListenFor RequireAppName="BeforePhrase"> allume [moi] [la] lumière [de] [la] [du] {room}</ListenFor>
        <Feedback> J'allume la lumière {room}</Feedback>
        <VoiceCommandService Target="CortanaSampleService"/>
      </Command>

      <PhraseList Label="room">
        <Item>Salon</Item>
        <Item>Cuisine</Item>
        <Item>Chambre</Item>
      </PhraseList>

  </CommandSet>
</VoiceCommands>

 

Pour plus d’informations, je vous invite à consulter la document VCD 1.2 : https://msdn.microsoft.com/en-us/library/dn954977.aspx

VoiceCommandDefinitionManager

J'ai parlé plus tôt de l'inscription des commandes. En effet, la présence du fichier vcd dans votre projet n'est pas suffisant (voire même pas obligatoire du tout). Ce qui importe c'est de charger un fichier vcd dans le VoiceCommandDefinitionManager.
Cela se fait en exécutant le code suivant :

var vcdfile = await Package.Current.InstalledLocation.GetFileAsync(@"VoiceCommands.xml");
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdfile);

Ce code est en général appelé depuis la méthode OnLaunched de la class App pour être éxécuté au premier lancement, mais peut également être effectué ailleurs si vous modifiez la grammaire dans votre application.

 

Création du service de communication via un Windows Runtime Component

Vous aurez remarqué (ou pas) que les éléments Command contiennent une balise VoiceCommandService contenant une propriété Target.
Cette propriété permet en réalité de déporter le déclenchement des commandes vocales dans une background task, permettant ainsi de ne pas lancer l'application, ce qui est, je vous le rappelle, le but de cet article.

 

Implémenter la background task et les commandes

Notre service étant ciblé, il faut donc le créer. Pour cela, ajoutez un projet de type "Windows Runtime Component" à votre solution que nous nommerons "CortanaSampleService".
Pour en faire une background task, ajoutez une classe CortanaSampleService qui implémente l'interface IBackgroundTask. Cette classe doit être sealed car c'est un composant WinRT.

Ajoutez ensuite ce projet en référence au projet UWP.

La dernière étape importante de création du service est de le déclarer dans votre app UWP.
Dans le fichier Package.apppxmanifest, ouvrez l'onglet et ajoutez une déclaration de type App Service avec les propriétés suivantes :

Name : CortanaSampleService
Entrypoint : CortanaSampleService.CortanaSampleService

ServiceDeclaration

Cela va permettre à votre grammaire de savoir quel task lancer quand elle sera reconnue.

 

Deferral

Un point très important dans notre background task est qu'elle dépend de Cortana, peut être lancée plusieurs fois et annulée. Il faut donc gérer ces comportements avec un deferral dans la méthode Run de la classe CortanaSampleService.

// Get deferral
_serviceDeferral = taskInstance.GetDeferral();

// If cancelled, set deferral
taskInstance.Canceled += (sender, reason) => _serviceDeferral?.Complete();

_voiceCommandServiceConnection =
        VoiceCommandServiceConnection.FromAppServiceTriggerDetails(triggerDetails);
        
// Set deferal when voice command is completed
    _voiceCommandServiceConnection.VoiceCommandCompleted += (sender, args) => _serviceDeferral?.Complete();

 

Exécution des commandes

Une fois notre service démarré, nous allons pouvoir exécuter les actions associées.
Pour cela nous allons devoir commencer par récupérer la commande qui est à l'initiative de l'action.

// Get voice command
var voicecommand = await _voiceCommandServiceConnection.GetVoiceCommandAsync();

Selon le name de cette commande nous allons pouvoir déterminer quel comportement adopter et quelle action effectuer :

switch (voicecommand.CommandName)
{
    case "turnOnLight":
        // Get the room with selected name
        var roomName = voicecommand.Properties["room"][0].ToLower();
        // TODO turn on the light in the specific room
        break;
    case "showLightsList":
        // TODO show list of lights
        break;
}

 

La suite


Nous verrons dans un article suivant comment communiquer avec Cortana pour lui indiquer que l'action est effectuée, en cours de traitement ou même lever des ambigüités en lui affichant des listes d'éléments.

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus