Intégration d'une application UWP à Cortana (1 sur 2)
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.
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
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.
Commentaires