Intégration d'une application UWP à Cortana (2 sur 2)
Dans mon dernier article sur Cortana, nous avions vu comment réaliser l'intégration de Cortana à nos apps UWP. Nous nous étions arrêtés à l'exécution des commandes reconnues par Cortana.
Aujourd'hui, nous allons voir que nous pouvons intégrer notre application à l'expérience Cortana.
Notre objectif est de rentre votre application aussi intégrée à Windows que possible en utilisant les interactions intégrées à Cortana :
- information de progression (ReportProgressAsync),
- affichage d'une liste de choix (tuiles) pour lever les ambiguités (RequestDisambiguationAsync),
- demande de confirmation à l'utilisateur (RequestConfirmationAsync),
- information de succès de l'opération demandée (ReportSuccessAsync )
ReportProgressAsync
Imaginons que notre tâche soit un peu longue à être traitée. Nous pouvons informer l'utilisateur de ce délai via Cortana.
Pour cela nous allons utiliser la méthode ReportProgressAsync.
Créons une méthode SendProgressMessageAsync qui ordonnera à Cortana d'afficher une fenêtre de chargement avec un message personnalisé :
private async Task SendProgressMessageAsync(Light selectedLight) { var progressmessage = new VoiceCommandUserMessage(); progressmessage.DisplayMessage = progressmessage.SpokenMessage = $"Attends 2 secondes, je ne trouve plus la lumière {selectedLight.Name}"; // Show progress message var response = VoiceCommandResponse.CreateResponse(progressmessage); await _voiceCommandServiceConnection.ReportProgressAsync(response); }
Nous voyons dans cette méthode que pour créer un message de progression, avec loader etc, dans Cortana, nous devons tout d'abord créer cette réponse via la méthode VoiceCommandResponse.CreateResponse. Ce constructeur prend en paramètre un message qui sera affiché à l'utilisateur. Le message sera, quant à lui, une instance de la classe VoiceCommandUserMessage et sa propriété DisplayMessage sera utilisée.
RequestDisambiguationAsync
Dans certains cas, l'utilisateur ne sait pas exactement quelles sont les possibilités de phrases qui lui sont offertes et pourra prononcer des phrases ambigües. Il convient alors de les gérer et de lui proposer une liste de propositions afin de permettre à Cortana de définir avec précision la commande voulue par l'utilisateur.
Pour cela, le framework nous propose une méthode RequestDisambiguationAsync qui permet de proposer à l'utilisateur une liste d'items, sous forme de "tuiles", parmi lesquels il pourra choisir. Cela permet à l'utilisateur d'énoncer plus précisément son choix mais également à Cortana de connaître les items qu'elle attend et de plus facilement les discerner.
Illustrons ce cas dans le cadre de l’implémentation d’une méthode ShowLightsListAsync qui aura pour but de sélectionner une lampe particulière.
Récupération des items via la grammaire VCD
Nous allons afficher la liste des pièces/lumières disponibles. Dans notre exemple nous utiliserons une liste statique mais pour rendre la méthode dynamique nous pourrions utiliser la liste des pièces définies dans la grammaire en récupérant la liste via le code suivant : voicecommand.Properties["room"].
Cette liste de String sera utilisée en paramètre de la méthode ShowLightsListAsync.
Construction des tuiles à afficher
Pour afficher les items dans Cortana, il faut créer les tuiles associées. Nous allons donc créer une méthode GetTileAsync prenant en paramètre un objet de type Light.
private async Task<VoiceCommandContentTile> GetTileAsync(Light light) { // Build a tile from selected country flag return new VoiceCommandContentTile { ContentTileType = VoiceCommandContentTileType.TitleWith68x68IconAndText, AppContext = light, AppLaunchArgument = "selectedid=" + light, Title = light.Name, TextLine1 = light.Name, Image = await StorageFile.GetFileFromApplicationUriAsync( new Uri($"ms-appx:///CortanaSampleService/light.png")) }; }
Dans cette méthode, nous utilisons la classe VoiceCommandContentTile pour créer une Tile de type TitleWith68x68IconAndText et fournissons l'URI de l'image à afficher. Les paramètres pourront être construits dynamiquement dans un exemple plus concret pour afficher l'image de la pièce ou le statut allumé/éteint de la lumière par exemple. Nous fournissons également un id et un AppContext qui seront ceux récupérés via Cortana quand l'utilisateur fera son choix.
Maintenant que nous savons comment créer les tuiles, il nous faut les afficher. Nous allons donc remplir notre méthode ShowLightsListAsync, avec le code suivant :
private async Task<Light> ShowLightsListAsync(IReadOnlyList<string> lights) { // Prompt and repeat promt var promptmessage = new VoiceCommandUserMessage(); promptmessage.DisplayMessage = promptmessage.SpokenMessage = "Précisez une lumière pour l'allumer"; var repeatpromptmessage = new VoiceCommandUserMessage(); repeatpromptmessage.DisplayMessage = promptmessage.SpokenMessage = "Quelle lumière voulez vous allumer ?"; var tilelist = new List<VoiceCommandContentTile>(); // Get tile for each room light foreach (var light in lights) { var tile = await GetTileAsync(Lights.First(l => l.Name == light)); tilelist.Add(tile); } // Create a prompt response message var response = VoiceCommandResponse.CreateResponseForPrompt(promptmessage, repeatpromptmessage, tilelist); // Show and get answer from user var result = await _voiceCommandServiceConnection.RequestDisambiguationAsync(response); // Return the selected item (or null) return result?.SelectedItem.AppContext as Light; }
Et voilà, notre méthode délègue à Cortana l'affichage des différents items et récupère celui choisi par l'utilisateur.
L'élément 1 correspond à l'icône de votre application, le 2 à la liste de tuiles et le 3 aux indications de Cortana pour guider l'utilisateur.
RequestConfirmationAsync
Vous avez encore un doute ? Vous avez un autre scénario nécessitant d'être sûr de la sélection de l'utilisateur ? Proposons à Cortana de confirmer le choix.
Créons la méthode PromptForConfirmationAsync(Light selectedLight) avec le code suivant :
private async Task<bool> PromptForConfirmationAsync(Light selectedLight) { // Pompt and repeat promt var confirmationmessage = new VoiceCommandUserMessage(); confirmationmessage.DisplayMessage = confirmationmessage.SpokenMessage = string.Format($"Voulez vous vraiment allumer la lumière {selectedLight.Name}"); var repeatconfirmationmessage = new VoiceCommandUserMessage(); repeatconfirmationmessage.DisplayMessage = repeatconfirmationmessage.SpokenMessage = "fffff"; // Get flag tile var tile = await GetTileAsync(selectedLight); // Ask for confirmation // Demande la confirmation var response = VoiceCommandResponse.CreateResponseForPrompt(confirmationmessage, repeatconfirmationmessage, new List<VoiceCommandContentTile> { tile }); var confirmation = await _voiceCommandServiceConnection.RequestConfirmationAsync(response); return confirmation?.Confirmed ?? false; }
Cette méthode utilise l'item sélectionné et les méthodes GetTileAsync et CreateResponse pour créer un message de demande de confirmation à l'utilisateur. La méthode RequestConfirmationAsync utilise ensuite ce message pour l'afficher via Cortana et récupérer un objet de type VoiceCommandeConfirmationResult.
ReportSuccessAsync
Que l'on ait demandé à l'utilisateur de choisir dans une liste, que sa sélection ait été directe, ou confirmée, que ce traitement nécessite un message de progression ou pas, l'important dans l'expérience utilisateur et de lui confirmer le succès du traitement. Pour cela, nous utiliserons la méthode ReportSuccessAsync du service de commandes vocales.
Créons donc la méthode SendSuccessMessageAsync prenant en paramètre un objet de type Light.
private async Task SendSuccessMessageAsync(Light selectedLight) { var confirmationmessage = new VoiceCommandUserMessage(); // Get selected flag tile var tile = await GetTileAsync(selectedLight); confirmationmessage.DisplayMessage = confirmationmessage.SpokenMessage = $"Ok. J'allume la lumière {selectedLight.Name}"; // Show the success message var response = VoiceCommandResponse.CreateResponse(confirmationmessage, new List<VoiceCommandContentTile> { tile }); await _voiceCommandServiceConnection.ReportSuccessAsync(response); }
Nous voyons que nous pouvons construire dynamiquement le message de confirmation qui sera énoncé par Cortana et que celui-ci peut à nouveau utiliser la méthode de construction de tuile pour un affichage plus complet.
Intégration au traitement Cortana
Une fois ces méthodes créées, vous pourrez alors améliorer un peu l'expérience utilisateur et appeler ces méthodes SendProgressMessageAsync, ShowLightsListAsync, PromptForConfirmationAsync et SendSuccessMessageAsync dans votre traitement global Run. Voici un exemple de mise à jour du code du précédent article :
switch (voicecommand.CommandName) { case "turnOnLight": // Get the room with selected name var roomName = voicecommand.Properties["room"][0].ToLower(); var lights = Lights.Where(d => d.Name.ToLower().Contains(roomName)).ToList(); selectedLight = lights.First(); await SendProgressMessageAsync(selectedLight); //TODO Send a message to turn the light on await Task.Delay(2000); await SendSuccessMessageAsync(selectedLight); break; case "showLightsList": //Get lights from vcd var vcdLights = Lights; // create a static list of Light //Show lights list selectedLight = await ShowLightsListAsync(vcdLights); if (selectedLight != null) { //If one light is selected, ask confirmation if (await PromptForConfirmationAsync(selectedLight)) { // Send success message with selected light await SendSuccessMessageAsync(selectedLight); } } break; }
Conclusion
Nous avons vu avec ces exemples que nous pouvons intégrer notre application et ses traitements à Windows via Cortana et la commande vocale. Cette intégration complète permet de cibler et d'effectuer les traitements sans jamais sortir de Cortana et sans avoir à lancer notre application.
A vous désormais de construire votre application et la gestion globale de commande de celle-ci par Cortana.
Commentaires