Intermédiaire

Faire interagir votre application Windows 10 avec Cortana

5258Nous avons vu, dans les précédents articles, comment intégrer du contenu directement au sein de Cortana via un service de notre application, nous allons désormais voir comment interagir de manière un peu plus poussée.

Le but de cet article est de vous présenter des fonctionnalités qui vous permettront d’étendre grandement vos scénarii d’interaction avec Cortana, mais aussi d’impliquer votre utilisateur de manière plus poussée.

Introduction

Nous allons donc désormais découvrir trois fonctionnalités intéressantes de Cortana :

  • L’écran de confirmation : imaginez que vous demandiez à Cortana de supprimer une série de vos favoris, vous pourriez alors vouloir une confirmation de manière à être sûr de ce qu’a compris Cortana.
  • La levée d’ambigüité : imaginez que vous demandiez à Cortana de vous afficher la série « Heroes ». Or il existe deux séries : « Heroes » et « Heroes Reborn ». Vous pouvez alors afficher un écran dont le but est de demander à l’utilisateur de faire un choix.
  • Le message d’erreur  : comme son nom l’indique, permet d’afficher un message d’erreur personnalisé lorsque vous le souhaitez.

Pour commencer, il va être nécessaire de repartir du projet que nous avons créé au sein de notre second article.

Nous allons aborder ce dernier article d’une manière un peu différente des deux précédents. Je vais tout d’abord vous fournir le code complet de la fonction ShowSerie, qui remplacera la fonction que nous avons créée au sein du second article. Nous l’analyserons ensuite de manière à comprendre les trois fonctionnalités que nous désirons aborder.

Voici donc le code de la nouvelle fonction ShowSerie qui doit être placée au sein du Voice Command Service :

private async Task ShowSerie(VoiceCommand command) 
{ 
    //Création d'un message de réponse temporaire permettant de ne plus couper la tâche au bout de .5 secondes par défaut mais au bout de 5 secondes 
    var userProgressMessage = new VoiceCommandUserMessage(); 
    userProgressMessage.DisplayMessage = userProgressMessage.SpokenMessage = "Nous récupérons vos séries"; 
 
    VoiceCommandResponse response_temp = VoiceCommandResponse.CreateResponse(userProgressMessage); 
    await voiceServiceConnection.ReportProgressAsync(response_temp); 
 
    string serie = command.Properties["serie"][0]; 
 
    var selectedSeries = _series.Where(x => x.Value.Contains(serie)); 
 
    if (selectedSeries.Count() == 1) 
    { 
        //Si il y a seulement une série correspondante, alors nous allons demander une confirmation (juste pour l'exemple) 
        var userPrompt = new VoiceCommandUserMessage(); 
        userPrompt.SpokenMessage = userPrompt.DisplayMessage = "Est-ce bien la série que vous voulez afficher?"; 
 
        var userReprompt = new VoiceCommandUserMessage(); 
        userReprompt.SpokenMessage = userReprompt.DisplayMessage = "Confirmez-vous que c'est bien la série que vous voulez afficher?"; 
 
 
        //Création de la liste de résultats à afficher par Cortana 
        var destinationsContentTiles = new List(); 
 
        var tile = new VoiceCommandContentTile(); 
        tile.ContentTileType = VoiceCommandContentTileType.TitleOnly; 
        tile.AppLaunchArgument = "serie-" + selectedSeries.First().Key; 
        tile.Title = selectedSeries.First().Value; 
 
        destinationsContentTiles.Add(tile); 
 
        var response = VoiceCommandResponse.CreateResponseForPrompt(userPrompt, userReprompt, destinationsContentTiles); 
 
        response.AppLaunchArgument = "serie-"+selectedSeries.First().Key; 
 
        var responseConfirmation = await voiceServiceConnection.RequestConfirmationAsync(response); 
 
        if(responseConfirmation != null && responseConfirmation.Confirmed) 
        { 
            var userMessage = new VoiceCommandUserMessage(); 
            userMessage.SpokenMessage = "Lancement de l'application"; 
 
            var tempesponse = VoiceCommandResponse.CreateResponse(userMessage); 
 
            response.AppLaunchArgument = "serie-" + selectedSeries.First().Key; 
 
            await voiceServiceConnection.RequestAppLaunchAsync(response); 
        } 
        else 
        { 
            //Si ce n'est pas cette série, alors on affiche les autres séries 
            ShowSeries(); 
        } 
    } 
    else if (selectedSeries.Count() > 1) 
    { 
        //Levée d'ambigüité 
 
        //Création de la liste de résultats à afficher par Cortana 
        var destinationsContentTiles = new List(); 
 
        //Création d'une tuile pour chaque série 
        foreach (var tempSerie in selectedSeries) 
        { 
            var tile = new VoiceCommandContentTile(); 
            tile.ContentTileType = VoiceCommandContentTileType.TitleOnly; 
            tile.AppLaunchArgument = "serie-" + tempSerie.Key; 
            tile.Title = tempSerie.Value; 
 
            destinationsContentTiles.Add(tile); 
        } 
 
        var userPrompt = new VoiceCommandUserMessage(); 
        userPrompt.DisplayMessage = "Afficher une série"; 
        userPrompt.SpokenMessage = "Précisez la série que vous voulez afficher"; 
 
        var userReprompt = new VoiceCommandUserMessage(); 
        userReprompt.DisplayMessage = "Quelle série voulez-vous afficher?"; 
        userReprompt.SpokenMessage = "Quelle série voulez-vous afficher?"; 
 
        var disambiguationResponse = VoiceCommandResponse.CreateResponseForPrompt(userPrompt, userReprompt, destinationsContentTiles); 
 
        var result = await voiceServiceConnection.RequestDisambiguationAsync(disambiguationResponse); 
 
        if (result != null) 
        { 
            var userMessage = new VoiceCommandUserMessage(); 
            userMessage.SpokenMessage = "Lancement de l'application"; 
 
            var response = VoiceCommandResponse.CreateResponse(userMessage); 
 
            response.AppLaunchArgument = result.SelectedItem.AppLaunchArgument; 
 
            await voiceServiceConnection.RequestAppLaunchAsync(response); 
        } 
    } 
    else 
    { 
        //Message d'erreur 
        var errorMessage = new VoiceCommandUserMessage(); 
        errorMessage.DisplayMessage = "Aucune série ne correspond"; 
        errorMessage.SpokenMessage = "Aucune série ne correspond"; 
 
        var response = VoiceCommandResponse.CreateResponse(errorMessage); 
 
        await voiceServiceConnection.ReportFailureAsync(response); 
    } 
             
} 

La première partie de cette fonction est similaire à notre précédente fonction. Nous récupérons les arguments de la commande vocale en cours, puis nous affichons un message d’attente à l’utilisateur de manière à pouvoir réaliser les actions désirées dans un délai plus important. Ce message d’attente ressemblera dans notre cas à ceci :

cortana-wait

Analysons désormais les différentes parties de ce code.

L’écran de confirmation

La première partie de cette fonction est dédiée à l’écran de confirmation.

Dans le cas où une seule série correspondrait à ce que nous avons demandé, il peut être intéressant de demander une confirmation à l’utilisateur (comme dans le cas d’une action de suppression par exemple).
La partie qui nous intéresse est celle qui est contenue dans la condition :

if (selectedSeries.Count() == 1) 
{ 
//… 
} 

Ce cas représente donc l’éventualité d’une seule série correspondante à la demande.

Nous allons donc créer un message pour demander à l’utilisateur si c’est bien cette série qu’il veut afficher  :

var userPrompt = new VoiceCommandUserMessage(); 
        userPrompt.SpokenMessage = userPrompt.DisplayMessage = "Est-ce bien la série que vous voulez afficher?"; 
 
var userReprompt = new VoiceCommandUserMessage(); 
        userReprompt.SpokenMessage = userReprompt.DisplayMessage = "Confirmez-vous que c'est bien la série que vous voulez afficher?"; 
 

Puis, nous allons créer une tuile qui correspond à la série et l’afficher au sein de l’assistant Cortana :

//Création de la liste de résultats à afficher par Cortana 
var destinationsContentTiles = new List(); 
 
var tile = new VoiceCommandContentTile(); 
tile.ContentTileType = VoiceCommandContentTileType.TitleOnly; 
tile.AppLaunchArgument = "serie-" + selectedSeries.First().Key; 
tile.Title = selectedSeries.First().Value; 
 
destinationsContentTiles.Add(tile); 
 
var response = VoiceCommandResponse.CreateResponseForPrompt(userPrompt, userReprompt, destinationsContentTiles); 
response.AppLaunchArgument = "serie-"+selectedSeries.First().Key; 
 

Une fois le message créé, il va falloir l’afficher. Pour ce faire, nous allons utiliser la fonction RequestConfrmationAsync qui permet de demander une confirmation à l’utilisateur :

var responseConfirmation = await voiceServiceConnection.RequestConfirmationAsync(response); 
 
Enfin, nous allons ajouter deux comportements différents. Si l’utilisateur a accepté, alors nous allons lancer l’application sur la bonne série. Dans le cas contraire, nous afficherons la liste des séries : 
if(responseConfirmation != null && responseConfirmation.Confirmed) 
{ 
var userMessage = new VoiceCommandUserMessage(); 
       userMessage.SpokenMessage = "Lancement de l'application"; 
 
var tempesponse = VoiceCommandResponse.CreateResponse(userMessage); 
 
       response.AppLaunchArgument = "serie-" + selectedSeries.First().Key; 
 
       await voiceServiceConnection.RequestAppLaunchAsync(response); 
} 
else 
{ 
//Si ce n'est pas cette série, alors on affiche les autres séries 
       ShowSeries(); 
} 

Voici l’écran qui devrait apparaître si vous tombez sur ce cas :
cortana-confirmation

Nous avons donc vu au sein de cette partie comment demander une confirmation à l’utilisateur.

Levée d’ambigüité

Nous allons donc, dans un second temps, nous attaquer à l’écran de levée d’ambigüité. Ce dernier permet d’afficher plusieurs résultats sur lesquels pourra cliquer l’utilisateur pour indiquer son choix. Nous allons donc choisir le cas des séries « Heroes » et « Heroes Reborn ». Lorsque l’utilisateur demandera « SOAT, affiche-moi la série Heroes », nous devrons lui demander s’il parle de « Heroes » ou « Heroes Reborn ».

Nous utilisons encore une fois la méthode ShowSerie, mais cette fois, nous nous occupons du cas :

if (selectedSeries.Count() > 1) 
{ 
//... 
} 

Nous allons donc commencer par créer une liste de tuiles qui représentent chacune une série qui pourrait avoir été demandée. Pour ce faire, nous utilisons le code suivant qui doit désormais vous être familier :

//Création de la liste de résultats à afficher par Cortana 
var destinationsContentTiles = new List(); 
 
//Création d'une tuile pour chaque série 
foreach (var tempSerie in selectedSeries) 
{ 
       var tile = new VoiceCommandContentTile(); 
       tile.ContentTileType = VoiceCommandContentTileType.TitleOnly; 
       tile.AppLaunchArgument = "serie-" + tempSerie.Key; 
       tile.Title = tempSerie.Value; 
 
destinationsContentTiles.Add(tile); 
} 

Ensuite, nous allons devoir afficher cette liste de tuiles pour demander à l’utilisateur laquelle est correcte. Pour ce faire, nous devons donc créer deux messages de réponse à lui afficher au sein de Cortana :

var userPrompt = new VoiceCommandUserMessage(); 
userPrompt.DisplayMessage = "Afficher une série"; 
userPrompt.SpokenMessage = "Précisez la série que vous voulez afficher"; 
 
var userReprompt = new VoiceCommandUserMessage(); 
userReprompt.DisplayMessage = "Quelle série voulez-vous afficher?"; 
userReprompt.SpokenMessage = "Quelle série voulez-vous afficher?"; 
 
var disambiguationResponse = VoiceCommandResponse.CreateResponseForPrompt(userPrompt, userReprompt, destinationsContentTiles); 

Enfin, il va nous falloir afficher cette liste de propositions et ces messages. Pour ce faire, nous allons faire appel à la méthode RequestDisambiguationAsync :

var result = await voiceServiceConnection.RequestDisambiguationAsync(disambiguationResponse); 

Une fois ceci fait, nous recevrons donc une réponse au sein de la variable result. En fonction de cette réponse, nous lancerons l’application avec le bon argument. Le code permettant de gérer la réponse de l’utilisateur est le suivant :

if (result != null) 
{ 
var userMessage = new VoiceCommandUserMessage(); 
userMessage.SpokenMessage = "Lancement de l'application"; 
 
       var response = VoiceCommandResponse.CreateResponse(userMessage); 
 
       response.AppLaunchArgument = result.SelectedItem.AppLaunchArgument; 
 
       await voiceServiceConnection.RequestAppLaunchAsync(response); 
} 

En demandant à Cortana “Soat, affiche-moi la série Heroes”, vous devriez donc avoir le résultat suivant :
cortana-ambiguite

Nous avons donc vu au sein de cette partie la manière de gérer un cas où notre application aurait besoin de plus d’informations.

Message d’erreur

Au sein de cette dernière partie, nous allons voir enfin le cas le plus simple : comment afficher un message d’erreur personnalisé à l’utilisateur.

Pour ce faire, nous nous occuperons du cas :

else 
    { 
        //Message d'erreur 
        var errorMessage = new VoiceCommandUserMessage(); 
        errorMessage.DisplayMessage = "Aucune série ne correspond"; 
        errorMessage.SpokenMessage = "Aucune série ne correspond"; 
 
        var response = VoiceCommandResponse.CreateResponse(errorMessage); 
 
        await voiceServiceConnection.ReportFailureAsync(response); 
    } 

En effet, dans notre cas, ceci peut se produire si une série est renseignée dans la PhraseList du fichier de Commande (VCD.xml), mais qu’il n’est pas renseigné au sein de notre BackgroundAgent. Ceci peut par exemple se produire si votre fichier VCD n’a pas été mis à jour depuis longtemps. Alors, il faudra afficher à l’utilisateur un message personnalisé.

Pour ce faire, nous devons créer comme d’habitude un VoiceCommandUserMessage, et cette fois, nous ferons appel à la méthode ReportFailureAsync de notre Voice Command Service.

Rien de bien compliqué donc pour ce dernier cas, voici ce que vous devriez avoir :
cortana-error

Conclusion

Nous avons vu, au sein de cet article, trois manières d’interagir avec l’utilisateur, et notamment de pouvoir lui demander des précisions ou une confirmation. Ces deux fonctionnalités ouvrent de larges possibilités pour votre application. Il est en effet tout à fait possible d’imaginer des scénarios ne nécessitant plus l’ouverture de votre application (comme réserver un billet d’avion par exemple).

Eh bien voilà ! Cette série de trois articles est désormais terminée, j’espère que nous avons couvert les cas les plus génériques et que cette série vous aura correctement accompagné.

Vous retrouverez l’intégralité du code source sur le GitHub de Soat (les projets qui vous intéresseront sont les projets Cortana.Interaction et Cortana.Interaction.CortanaAgent).

© SOAT
Toute reproduction interdite sans autorisation de la société SOAT

Nombre de vue : 282

AJOUTER UN COMMENTAIRE