昨天,老周演示了語音命令集成這一高大上功能,今天我們來點更高級的語音命令。windows
在昨天的例子中,響應語音命令是須要啓動應用程序的,那麼若是能夠不啓動應用程序,就直接在小娜面板上進行交互,是否是會更高大小呢。app
面向Win 10的API給應用程序增長了一種叫App Service的技術,應用程序能夠經過App Service公開服務來讓其餘應用程序調用。App Service是經過後臺任務來處理的,故不須要啓動應用程序,調用者只須要知道提供服務的應用程序的程序包名稱,以及要調用的服務名稱便可以進行調用了。關於App Service,老周曾作過相關視頻,有時間的話再補上博文。async
正由於App Service是經過後臺任務來處理的,再與小娜語音命令一集成,應用程序就能夠在後臺響應語音操做,而沒必要在前臺啓動。測試
好了,基本理論依據有了,接下來,老規矩,老周向來不喜歡講XYZ理論的,仍是直接說說如何用吧。ui
一、定義語音命令文件。老周寫了個新的文件。spa
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2"> <CommandSet xml:lang="zh-hans"> <AppName>樂器收藏</AppName> <Example>「樂器收藏 展示列表」,或者「樂器收藏 顯示列表」</Example> <Command Name="show"> <Example>展示列表,或者 顯示列表</Example> <ListenFor>[展示]列表</ListenFor> <ListenFor>顯示列表</ListenFor> <VoiceCommandService Target="vcfav"/> </Command> </CommandSet> </VoiceCommands>
其餘元素我在上一篇爛文中已經介紹過,不過你們會發現有個傢伙比較陌生——VoiceCommandService元素。對,實現語音命令和App Service集成,這個元素的配很關鍵,Target屬性就是你要集成的App Service的名字,本例子應用待會要公開的一個App Service名字叫vcfav,記好了。code
這個應用的用途是向你們Show一下老周收藏的幾款樂器,都是高大上的樂器,能奏出醉人心絃的仙樂。注意,這裏的命令文件用了VoiceCommandService元素,就不須要用Navigate元素。視頻
二、實現後臺任務。在解決方案中添加一個Runtime組件項目,記得老周N年前說過,實現後臺的類型是放到一個運行時組件項目中的。xml
public sealed class BTask : IBackgroundTask { BackgroundTaskDeferral taskDerral = null; VoiceCommandServiceConnection serviceConnection = null; public async void Run(IBackgroundTaskInstance taskInstance) {
後臺任務的功能固然是響應小娜收到的語音命令,由於能夠經過App service來觸發,因此咱們就能在後臺任務中進行交互。對象
要與小娜面板進行交互,咱們須要一個鏈接類——VoiceCommandServiceConnection類,它的實例能夠從後臺任務實例的觸發器數據中得到,就是這樣:
public async void Run(IBackgroundTaskInstance taskInstance) { taskDerral = taskInstance.GetDeferral(); AppServiceTriggerDetails details = taskInstance.TriggerDetails as AppServiceTriggerDetails; // 驗證是否調用了正確的app service if (details == null || details.Name != "vcfav") { taskDerral.Complete(); return; } serviceConnection = VoiceCommandServiceConnection.FromAppServiceTriggerDetails(details);
關鍵是這句:serviceConnection = VoiceCommandServiceConnection.FromAppServiceTriggerDetails(details);
鏈接對象就是這樣獲取的。
三、以後,咱們就能夠在代碼中與小娜交互了。在交互過程當中,發送到小娜面板的消息都由VoiceCommandUserMessage類來封裝,它有兩個屬性:
DisplayMessage:要顯示在小娜面板上的文本。
SpokenMessage:但願小娜說出來的文本。
若是你但願小娜說出的內容和麪板上顯示的內容相同,也能夠把這兩個屬性設置爲相同的文本。
與小娜交互的操做天然是由VoiceCommandServiceConnection實例來完成了,否則咱們上面獲取它幹嘛呢,就是爲了在後面的交互操做中使用。
VoiceCommandServiceConnection經過如下幾個方法來跟小娜交互:
ReportSuccessAsync:告訴小娜,處理已經完成,並返回一條消息,傳遞給小娜面板。
ReportFailureAsync:向小娜反饋錯誤信息。
ReportProgressAsync:報告進度,不指定具體進度值,只是在小娜面板上會顯示長達5秒鐘的進度條,你的代碼處理不該該超過這個時間,否則用戶體驗很差。最好控制在2秒鐘以內。
RequestAppLaunchAsync:請求小娜啓動當前應用。
RequestConfirmationAsync:向小娜面板發送一條須要用戶確認的消息。好比讓小娜問用戶:「你肯定還沒吃飯?」或者:「你認爲老周很帥嗎?」,用戶只需回答Yes or No。後臺任務會等待用戶的確認結果,以決定下一步作什麼。
RequestDisambiguationAsync:一樣,也是向用戶發出一條詢問消息,與上面的方法不一樣的是,這個方法會在小娜面板上列出一串東西,讓用戶說出選擇哪一項。好比,「請選擇你要看的電影:」,而後選項有:《弱智仙俠》、《花錢骨》、《燒腦時代》、《菊花傳奇》,你說出要選擇的項,或者點擊對應的項,小娜會把用戶選擇的項返回給應用程序後臺任務,以作進一步處理。
在老周這個示例中,若是語音命令被識別,就會在小娜面板上列出老周收藏的五件樂器,而後你能夠選擇一件進行收藏,固然是不包郵的,你還要付等價費。
if (serviceConnection != null) { serviceConnection.VoiceCommandCompleted += ServiceConnection_VoiceCommandCompleted; // 獲取被識別的語音命令 VoiceCommand cmd = await serviceConnection.GetVoiceCommandAsync(); if (cmd.CommandName == "show") { // 獲取測試數據,用於生成磁塊列表 var tiles = await BaseData.GetData(); // 定義返回給小娜面板的消息 VoiceCommandUserMessage msgback = new VoiceCommandUserMessage(); msgback.DisplayMessage = msgback.SpokenMessage = "請選擇要收藏的樂器。"; // 第二消息,必須項 VoiceCommandUserMessage msgRepeat = new VoiceCommandUserMessage(); msgRepeat.DisplayMessage = msgRepeat.SpokenMessage = "請選擇你要收藏的樂器。"; // 把消息發回到小娜面板,待用戶選擇 VoiceCommandResponse response = VoiceCommandResponse.CreateResponseForPrompt(msgback, msgRepeat, tiles); VoiceCommandDisambiguationResult selectedRes = await serviceConnection.RequestDisambiguationAsync(response); // 看看用戶選了什麼 VoiceCommandContentTile selecteditem = selectedRes.SelectedItem; // 保存已選擇的樂器 SaveSettings(selecteditem.Title); // 回傳給小娜面板,報告本次操做完成 msgback.DisplayMessage = msgback.SpokenMessage = "好了,你收藏了" + selecteditem.Title + "。"; response = VoiceCommandResponse.CreateResponse(msgback); await serviceConnection.ReportSuccessAsync(response); taskDerral.Complete(); } }
SaveSettings方法是把用戶選擇的收藏保存到應用程序本地設置中,以便在前臺應用中訪問。
private void SaveSettings(object value) { ApplicationDataContainer data = ApplicationData.Current.LocalSettings; data.Values["fav"] = value; }
在調用RequestDisambiguationAsync方法向小娜面板添加可供選擇的列表項時,必定要注意一點,做爲方法參數的VoiceCommandResponse實例必定要使用CreateResponseForPrompt靜態方法來建立,由於上面說過,提供有待用戶確認的交互有兩類:一類是yes or no,另外一類就是從列表中選一項。此處就是後者。
這裏老周也定義了一個BaseData類,用來產生顯示在小娜面板上的項的圖標,用VoiceCommandContentTile類來封裝,每一個VoiceCommandContentTile實例就是一個列表項,顯示的格式由ContentTileType屬性來指定,好比顯示純文本,仍是顯示圖標加文本,爲了讓你們看清楚老周的收藏品,此處選用圖標 + 文本的方式呈現。
internal class BaseData { public static async Task<IEnumerable<VoiceCommandContentTile>> GetData() { IList<VoiceCommandContentTile> tiles = new List<VoiceCommandContentTile>(); // 獲取數據 var filedatas = await GetFiles(); // 添加磁塊列表 for (uint n = 0; n < filedatas.Length; n++) { if (tiles.Count >= VoiceCommandResponse.MaxSupportedVoiceCommandContentTiles) { break; } VoiceCommandContentTile tile = new VoiceCommandContentTile(); tile.ContentTileType = VoiceCommandContentTileType.TitleWith68x68IconAndText; tile.Image = filedatas[n].Item1; tile.Title = filedatas[n].Item2; tile.TextLine1 = filedatas[n].Item3; tiles.Add(tile); } return tiles.ToArray(); ; } private async static Task<Tuple<StorageFile, string, string>[]> GetFiles() { string uh = "ms-appx:///Assets/"; // 笛子 Uri u1 = new Uri(uh + "笛子.png"); // 鼓 Uri u2 = new Uri(uh + "鼓.png"); // 大提琴 Uri u3 = new Uri(uh + "大提琴.png"); // 二胡 Uri u4 = new Uri(uh + "二胡.png"); // 古琴 Uri u5 = new Uri(uh + "古琴.png"); // 獲取文件對象 StorageFile imgFile1 = await StorageFile.GetFileFromApplicationUriAsync(u1); StorageFile imgFile2 = await StorageFile.GetFileFromApplicationUriAsync(u2); StorageFile imgFile3 = await StorageFile.GetFileFromApplicationUriAsync(u3); StorageFile imgFile4 = await StorageFile.GetFileFromApplicationUriAsync(u4); StorageFile imgFile5 = await StorageFile.GetFileFromApplicationUriAsync(u5); // 建立三元組列表 Tuple<StorageFile, string, string> item1 = new Tuple<StorageFile, string, string>(imgFile1, "笛子", "聲音空遠悠揚,靈動飄逸。"); Tuple<StorageFile, string, string> item2 = new Tuple<StorageFile, string, string>(imgFile2, "鼓", "樂聲雄渾,勁力深透。"); Tuple<StorageFile, string, string> item3 = new Tuple<StorageFile, string, string>(imgFile3, "大提琴", "音質宏厚。"); Tuple<StorageFile, string, string> item4 = new Tuple<StorageFile, string, string>(imgFile4, "二胡", "意綿綿,略帶悽婉。"); Tuple<StorageFile, string, string> item5 = new Tuple<StorageFile, string, string>(imgFile5, "古琴", "音質沉厚,古樸淡雅,可傳情達意。"); return new Tuple<StorageFile, string, string>[] { item1, item2, item3, item4, item5 }; } }
四、回到主項目,引用剛纔寫完的後臺任務。有的朋友說後臺任務不起做用,若是後臺類沒問題的話,可能的兩個問題是:a、主項目沒有引用後臺任務類所在的項目;b、清單文件沒有配置好。
五、最後,不要忘了配置清單文件,打開Package.appxmanifest文件,找到Application節點。
<Extensions> <uap:Extension Category="windows.appService" EntryPoint="BgService.BTask"> <uap:AppService Name="vcfav"/> </uap:Extension> </Extensions>
擴展點的Category屬性要指定windows.appService,表示擴展類型爲App Service,EntryPoint指定入口點,即後臺任務類的名字,包括命名空間和類型名。
在App類的OnLaunched方法中,記得安裝VCD文件。
StorageFile vcdfile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///vcd.xml")); await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdfile);
如今,你能夠測試了。運行應用程序,而後對着小娜說「收藏樂器 顯示列表」,而後給出選擇列表。
識別後,顯示操做結果。
源代碼下載地址:http://files.cnblogs.com/files/tcjiaan/VoicecmdWithrespApp.zip