在 Cortana 中使用语音命令激活后台应用

警告

自 Windows 10 2020 年 5 月更新(版本 2004,codename“20H1”)起,不再支持此功能。

除了在 Cortana 中使用语音命令来访问系统功能之外,你还可以使用指定要运行的操作或命令的语音命令来扩展 Cortana,使其具有应用中的特性和功能(作为后台任务)。 当应用在后台处理语音命令时,它不会获得焦点。 相反,它会通过 Cortana 画布和 Cortana 语音返回所有反馈和结果。

应用程序可能会被激活到前台(应用获得焦点)或在后台激活(Cortana 保持焦点),具体取决于交互的复杂性。 例如,需要额外上下文或用户输入(例如将消息发送给特定联系人)的命令最好通过前台应用处理,而基本命令(如列出即将到来的行程)可以通过后台应用使用 Cortana 处理。

如果要使用语音命令将应用激活到前台,请参阅通过 Cortana 使用语音命令激活前台应用

注意

语音命令是在语音命令定义 (VCD) 文件中定义且具有特定意图的单个言语,通过 Cortana 指向一个已安装的应用。

VCD 文件定义了一个或多个语音命令,每个命令都有一个独特的意图。

语音命令定义在复杂性方面可能各不相同。 这些定义可以支持任何内容,从单一的受约束的言语到更灵活的自然语言言语的集合,所有这些都表示相同的意图。

我们使用集成到 Cortana UI 中的名为 Adventure Works 应用的旅行计划和管理应用来演示我们讨论的许多概念和功能。 有关详细信息,请参阅 Cortana 语音命令示例

Cortana 启动前台应用的屏幕截图

如果没有 Cortana,要查看 Adventure Works 旅行,用户将启动应用并导航到“即将到来的旅行”页面。

通过 Cortana 使用语音命令在后台启动你的应用,用户可能只需要说,Adventure Works, when is my trip to Las Vegas? 你的应用处理命令,Cortana 显示结果以及你的应用图标和其他应用信息(如果提供)。

使用后台的 AdventureWorks 应用使用基本查询和结果屏幕的 Cortana 屏幕截图

以下基本步骤添加语音命令功能,并使用语音或键盘输入从你的应用中扩展 Cortana 的背景功能。

  1. 创建 Cortana 在后台调用的应用服务(请参阅 Windows.ApplicationModel.AppService)。
  2. 创建一个 VCD 文件。 VCD 文件是一个 XML 文档,它定义了用户在激活你的应用时可能说出的所有用于启动操作或调用命令的语音命令。 请参阅 VCD 元素和属性 v1.2
  3. 启动应用时,在 VCD 文件中注册命令集。
  4. 处理应用服务的后台激活和语音命令的运行。
  5. 在 Cortana 中显示并说出对语音命令的适当反馈。

提示

必备条件

如果你还不熟悉通用 Windows 平台 (UWP) 应用开发,请查看这些主题来熟悉此处讨论的技术。

用户体验指南

有关如何将你的应用与 Cortana 和语音交互集成的信息,请参阅 Cortana 设计指南,了解有关设计有用且具有吸引力的支持语音的应用的有用提示。

在 Visual Studio 中使用主项目创建新解决方案

  1. 启动 Microsoft Visual Studio 2015。
    此时将显示 Visual Studio 2015 开始页。

  2. 在“文件”菜单中,选择“新建”>“项目”。
    将显示“新建项目”对话框。 在该对话框的左侧窗格中,可以选择要显示的模板类型。

  3. 在左侧窗格中,展开“已安装”>“模板”>“Visual C#”>“Windows”,然后选择“通用”模板组。 对话框的中心窗格显示通用 Windows 平台 (UWP) 应用程序的项目模板列表。

  4. 在中心窗格中,选择“空白应用(通用 Windows)”模板
    “空白应用”模板创建了一个可以编译和运行的最小 UWP 应用。 “空白应用”模板不包含用户界面控件或数据。 你可以使用此页面作为指南将控件添加到应用。

  5. 在“名称”文本框中,键入你的项目名称。 示例:使用 AdventureWorks

  6. 单击“确定”按钮以创建项目。
    Microsoft Visual Studio 将创建该项目,并在“解决方案资源管理器”中显示该项目

将图像资源添加到主项目并在应用清单中指定它们

UWP 应用应该会自动选择最合适的图像。 选择基于特定设置和设备功能(高对比度、有效像素、区域设置等)。 你必须提供图像并确保在应用项目中为不同的资源版本使用适当的命名约定和文件夹组织。
如果你没有提供推荐的资源版本,那么用户体验可能会受到以下方面的影响。

  • 辅助功能
  • 本地化
  • 图像质量
    资源版本用于适应用户体验的以下变化。
  • 用户首选项
  • 功能
  • 设备类型
  • 位置

有关高对比度和比例因子的图像资源的更多详细信息,请访问“磁贴和图标资产指南”页面,网站为 msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets

必须使用限定符命名资源。 资源限定符是文件夹和文件名修饰符,用于标识应使用特定版本资源的上下文。

标准命名约定是 foldername/qualifiername-value[_qualifiername-value]/filename.qualifiername-value[_qualifiername-value].ext
示例:images/logo.scale-100_contrast-white.png,它可能仅使用根文件夹和文件名来引用代码:images/logo.png
有关详细信息,请访问“如何使用限定符命名资源页面”,网站为 msdn.microsoft.com/library/windows/apps/xaml/hh965324.aspx

Microsoft 建议你在字符串资源文件(例如 en-US\resources.resw)上标记默认语言,在图像上标记默认比例因子(例如 logo.scale-100.png),即使你当前不打算提供本地化或多分辨率资源。 但是,Microsoft 建议你至少提供 100、200 和 400 比例因子的资产。

重要

Cortana 画布标题区域中使用的应用图标是 文件中指定的 Square44x44Logo 图标。Package.appxmanifest
还可以为 Cortana 画布的内容区域中的每个条目指定一个图标。 结果图标的有效图像大小为:

  • 68w x 68h
  • 68w x 92h
  • 280w x 140h

在将 VoiceCommandResponse 对象传递给 VoiceCommandServiceConnection 类之前,不会验证内容图块。 如果你将 VoiceCommandResponse 对象传递给 Cortana,该对象包含带有不符合这些大小比例的图像的内容图块,则可能会发生异常。 

示例:Adventure Works 应用(VoiceCommandService\\AdventureWorksVoiceCommandService.cs) 使用 TitleWith68x68IconAndText 磁贴模板在 VoiceCommandContentTile 类上指定一个简单的灰色正方形 (GreyTile.png)。 徽标变体位于 VoiceCommandService\\Images 中,并使用 GetFileFromApplicationUriAsync 方法检索。

var destinationTile = new VoiceCommandContentTile();  

destinationTile.ContentTileType = VoiceCommandContentTileType.TitleWith68x68IconAndText;
destinationTile.Image = await StorageFile.GetFileFromApplicationUriAsync(
    new Uri("ms-appx:///AdventureWorks.VoiceCommands/Images/GreyTile.png")
);  

创建应用服务项目

  1. 右键单击你的解决方案名称,选择“新建”>“项目”。

  2. 在“已安装”>“模板”>“Visual C#”>“Windows”>“通用”下面,选择“Windows 运行时组件”。 Windows 运行时组件是实现应用服务 (Windows.ApplicationModel.AppService) 的组件。

  3. 键入项目的名称,然后单击“确定”按钮。
    示例:VoiceCommandService

  4. 在“解决方案资源管理器”中,选择 VoiceCommandService 项目并重命名 Visual Studio 生成的 Class1.cs 文件。 示例:Adventure Works 使用 AdventureWorksVoiceCommandService.cs

  5. 系统询问你是否要重命名所有出现的 Class1.cs 时,选择“是”按钮。

  6. AdventureWorksVoiceCommandService.cs 文件中:

    1. 添加以下 using 指令。
      using Windows.ApplicationModel.Background;
    2. 创建新项目时,项目名称将用作所有文件中的默认根命名空间。 重命名命名空间以将应用服务代码嵌套在主项目下。 示例:namespace AdventureWorks.VoiceCommands
    3. 在“解决方案资源管理器”中右键单击应用服务项目名称,然后选择“属性”。
    4. 在“库”选项卡上,使用相同的值更新默认命名空间字段。
      示例:AdventureWorks.VoiceCommands)。
    5. 创建实现 IBackgroundTask 接口的新类。 该类需要一个 Run 方法,这是 Cortana 识别语音命令时的入口点。

    示例:Adventure Works 应用中的基本后台任务类。

    注意

    后台任务类本身,以及后台任务项目中的所有类,都必须是密封的公共类。

    namespace AdventureWorks.VoiceCommands
    {
        ...
    
        /// <summary>
        /// The VoiceCommandService implements the entry point for all voice commands.
        /// The individual commands supported are described in the VCD xml file. 
        /// The service entry point is defined in the appxmanifest.
        /// </summary>
        public sealed class AdventureWorksVoiceCommandService : IBackgroundTask
        {
            ...
    
            /// <summary>
            /// The background task entrypoint. 
            /// 
            /// Background tasks must respond to activation by Cortana within 0.5 second, and must 
            /// report progress to Cortana every 5 seconds (unless Cortana is waiting for user
            /// input). There is no running time limit on the background task managed by Cortana,
            /// but developers should use plmdebug (https://msdn.microsoft.com/library/windows/hardware/jj680085%28v=vs.85%29.aspx)
            /// on the Cortana app package in order to prevent Cortana timing out the task during
            /// debugging.
            /// 
            /// The Cortana UI is dismissed if Cortana loses focus. 
            /// The background task is also dismissed even if being debugged. 
            /// Use of Remote Debugging is recommended in order to debug background task behaviors. 
            /// Open the project properties for the app package (not the background task project), 
            /// and enable Debug -> "Do not launch, but debug my code when it starts". 
            /// Alternatively, add a long initial progress screen, and attach to the background task process while it runs.
            /// </summary>
            /// <param name="taskInstance">Connection to the hosting background service process.</param>
            public void Run(IBackgroundTaskInstance taskInstance)
            {
              //
              // TODO: Insert code 
              //
              //
        }
      }
    }
    
  7. 在应用清单中将你的后台任务声明为 AppService。

    1. 在“解决方案资源管理器”中,右键单击 Package.appxmanifest 文件并选择“查看代码”。
    2. 查找 Application 元素。
    3. Extensions 元素添加到 Application 层中。
    4. uap:Extension 元素中添加一个 Extensions 元素。
    5. Category 属性添加到 uap:Extension 元素中,并将 Category 属性的值设置为 windows.appService
    6. EntryPoint 属性添加到 uap: Extension 元素并将 EntryPoint 属性的值设置为实现 IBackgroundTask 的类的名称。
      示例:AdventureWorks.VoiceCommands.AdventureWorksVoiceCommandService
    7. uap:AppService 元素中添加一个 uap:Extension 元素。
    8. Name 属性添加到 uap:AppService 元素中,并将 Name 属性的值设置为应用服务的名称,在此情况下是 AdventureWorksVoiceCommandService
    9. 将另一个 uap:Extension 元素添加到 Extensions 层中。
    10. Category 属性添加到 uap:Extension 元素中,并将 Category 属性的值设置为 windows.personalAssistantLaunch

    示例:Adventure Works 应用中的清单。

    <Package>
        <Applications>
            <Application>
    
                <Extensions>
                    <uap:Extension Category="windows.appService" EntryPoint="CortanaBack1.VoiceCommands.AdventureWorksVoiceCommandService">
                        <uap:AppService Name="AdventureWorksVoiceCommandService"/>
                    </uap:Extension>
                    <uap:Extension Category="windows.personalAssistantLaunch"/>
                </Extensions>
    
            <Application>
        <Applications>
    </Package>
    
  8. 将此应用服务项目添加为主项目中的引用。

    1. 右键单击“引用”。
    2. 选择“添加引用...”
    3. 在“引用管理器”对话框中,展开“项目”并选择应用服务项目。
    4. 单击“确定”按钮。

创建一个 VCD 文件

  1. 在 Visual Studio 中,右键单击你的主项目名称,选择“添加”>“新项”。 添加一个 XML 文件
  2. 键入 VCD 文件的名称。
    示例:AdventureWorksCommands.xml
  3. 单击“添加”按钮。
  4. 在“解决方案资源管理器”中,选择该 VCD 文件
  5. 在“属性”窗口中,将“生成操作”设置为“内容”,然后将“复制到输出目录”设置“如果较新则复制”

编辑 VCD 文件

  1. 添加一个 VoiceCommands 元素,其 xmlns 属性指向 https://schemas.microsoft.com/voicecommands/1.2

  2. 对于应用支持的每种语言,创建一个包含应用支持的语音命令的 CommandSet 元素。
    你可以声明多个 CommandSet 元素,每个元素都具有不同的 xml:lang 属性,因此你的应用可以在不同的市场中使用。 例如,美国的应用可能有英语的 CommandSet 和西班牙语的 CommandSet

    重要

    要激活应用并使用语音命令启动操作,应用必须注册一个 VCD 文件,该文件包含一个语言与用户设备中指示的语音语言相匹配的 CommandSet 元素。 语音语言位于“设置”>“系统”>“语音”>“语音语言”中

  3. 为你要支持的每个命令添加一个 Command 元素。
    VCD 文件中声明的每个 Command 都必须包含以下信息:

    • Name 属性,你的应用程序用于在运行时识别语音命令。

    • Example 元素,包含描述用户如何调用命令的短语。 Cortana 会在用户说出 What can I say?Help,或点击“查看更多”时显示示例。

    • 一个 ListenFor 元素,包含应用识别为命令的单词或短语。 每个 ListenFor 元素可能包含对一个或多个 PhraseList 元素的引用,这些元素包含与命令相关的特定单词。

      注意

      不得以编程方式修改 ListenFor 元素。 然而,与 ListenFor 元素相关联的 PhraseList 元素可以通过编程方式修改。 应用程序应根据用户使用应用时生成的数据集在运行时修改 PhraseList 元素的内容。

      有关详细信息,请参阅动态修改 Cortana VCD 短语列表

    • 一个 Feedback 元素,其中包含 Cortana 在应用程序启动时显示和朗读的文本。

Navigate 元素指示语音命令将应用激活到前台。 在此示例中,showTripToDestination 命令是前台任务。

VoiceCommandService 元素指示语音命令在后台激活应用。 Target 元素的属性值应与 package.appxmanifest 文件中 uap:AppService 元素的 Name 属性值匹配。 在此示例中,whenIsTripToDestinationcancelTripToDestination 命令是后台任务,将应用服务的名称指定为 AdventureWorksVoiceCommandService

有关更多详细信息,请参阅 VCD 元素和属性 v1.2 参考

示例:为 Adventure Works 应用定义语音命令的 VCD 文件的一部分。en-us

<?xml version="1.0" encoding="utf-8" ?>
<VoiceCommands xmlns="https://schemas.microsoft.com/voicecommands/1.2">
<CommandSet xml:lang="en-us" Name="AdventureWorksCommandSet_en-us">
    <AppName> Adventure Works </AppName>
    <Example> Show trip to London </Example>
    
    <Command Name="showTripToDestination">
        <Example> Show trip to London </Example>
        <ListenFor RequireAppName="BeforeOrAfterPhrase"> show [my] trip to {destination} </ListenFor>
        <ListenFor RequireAppName="ExplicitlySpecified"> show [my] {builtin:AppName} trip to {destination} </ListenFor>
        <Feedback> Showing trip to {destination} </Feedback>
        <Navigate />
    </Command>
      
    <Command Name="whenIsTripToDestination">
        <Example> When is my trip to Las Vegas?</Example>
        <ListenFor RequireAppName="BeforeOrAfterPhrase"> when is [my] trip to {destination}</ListenFor>
        <ListenFor RequireAppName="ExplicitlySpecified"> when is [my] {builtin:AppName} trip to {destination} </ListenFor>
        <Feedback> Looking for trip to {destination}</Feedback>
        <VoiceCommandService Target="AdventureWorksVoiceCommandService"/>
    </Command>
    
    <Command Name="cancelTripToDestination">
        <Example> Cancel my trip to Las Vegas </Example>
        <ListenFor RequireAppName="BeforeOrAfterPhrase"> cancel [my] trip to {destination}</ListenFor>
        <ListenFor RequireAppName="ExplicitlySpecified"> cancel [my] {builtin:AppName} trip to {destination} </ListenFor>
        <Feedback> Cancelling trip to {destination}</Feedback>
        <VoiceCommandService Target="AdventureWorksVoiceCommandService"/>
    </Command>

    <PhraseList Label="destination">
        <Item>London</Item>
        <Item>Las Vegas</Item>
        <Item>Melbourne</Item>
        <Item>Yosemite National Park</Item>
    </PhraseList>
</CommandSet>

安装 VCD 命令

你的应用必须运行一次才能安装 VCD。

注意

语音命令数据不会在应用安装中保留。 为确保你的应用的语音命令数据保持不变,请考虑在每次启动或激活你的应用时初始化你的 VCD 文件,或维护一个指示当前是否安装了 VCD 的设置。

app.xaml.cs 文件中:

  1. 添加以下 using 指令:

    using Windows.Storage;
    
  2. 使用 async 修饰符标记 OnLaunched 方法。

    protected async override void OnLaunched(LaunchActivatedEventArgs e)
    
  3. OnLaunched 处理程序中调用 InstallCommandDefinitionsFromStorageFileAsync 方法来注册应识别的语音命令。
    示例:Adventure Works应用定义了一个 StorageFile 对象。
    示例:调用 GetFileAsync 方法以使用 AdventureWorksCommands.xml 文件初始化 StorageFile 对象。
    然后将 StorageFile 对象传递给 InstallCommandDefinitionsFromStorageFileAsync 方法。

    try {
       // Install the main VCD. 
       StorageFile vcdStorageFile = await Package.Current.InstalledLocation.GetFileAsync(
             @"AdventureWorksCommands.xml"
       );
    
       await Windows.ApplicationModel.VoiceCommands.VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdStorageFile);
    
       // Update phrase list.
       ViewModel.ViewModelLocator locator = App.Current.Resources["ViewModelLocator"] as ViewModel.ViewModelLocator;
       if(locator != null) {
             await locator.TripViewModel.UpdateDestinationPhraseList();
         }
     }
     catch (Exception ex) {
         System.Diagnostics.Debug.WriteLine("Installing Voice Commands Failed: " + ex.ToString());
     }
    

处理激活

指定你的应用如何响应后续语音命令激活。

注意

安装语音命令集后,你必须至少启动一次应用。

  1. 确认应用已通过语音命令激活。

    重写 Application.OnActivated 事件并检查 IActivatedEventArgs.KindVoiceCommand

  2. 确定命令的名称和所说的内容。

    IActivatedEventArgs 获取对 VoiceCommandActivatedEventArgs 对象的引用并查询 SpeechRecognitionResult 对象的 Result 属性。

    要确定用户所说的内容,请检查 Text 的值或 SpeechRecognitionSemanticInterpretation 字典中已识别短语的语义属性。

  3. 在应用中采取适当的操作,例如导航到所需的页面。

    注意

    如果你需要参考你的 VCD,请访问编辑 VCD 文件部分。

    收到语音命令的语音识别结果后,你从 RulePath 数组中的第一个值中获取命令名称。 由于 VCD 文件定义了多个可能的语音命令,因此你必须验证该值是否与 VCD 中的命令名称匹配并采取适当的措施。

    应用程序最常见的操作是导航到内容与语音命令上下文相关的页面。
    示例:打开 TripPage 页面并传入语音命令的值、命令的输入方式以及识别的目标短语(如果适用)。 或者,当导航到 TripPage 页面时,应用可以向 SpeechRecognitionResult 发送导航参数。

    你可以使用 commandMode 键从 SpeechRecognitionSemanticInterpretation.Properties 字典中查明启动应用的语音命令是否实际被说出,或者是否以文本形式输入。 该键的值将是 voicetext。 如果键的值为 voice,请考虑在你的应用中使用语音合成 (Windows.Media.SpeechSynthesis) 为用户提供语音反馈。

    使用 SpeechRecognitionSemanticInterpretation.Properties 找出在 ListenFor 元素的 PhraseListPhraseTopic 约束中说出的内容。 字典键是 PhraseListPhraseTopic 元素的 Label 属性值。 示例:以下代码用于如何访问 {destination} 短语的值。

    /// <summary>
    /// Entry point for an application activated by some means other than normal launching. 
    /// This includes voice commands, URI, share target from another app, and so on. 
    /// 
    /// NOTE:
    /// A previous version of the VCD file might remain in place 
    /// if you modify it and update the app through the store. 
    /// Activations might include commands from older versions of your VCD. 
    /// Try to handle these commands gracefully.
    /// </summary>
    /// <param name="args">Details about the activation method.</param>
    protected override void OnActivated(IActivatedEventArgs args) {
        base.OnActivated(args);
    
        Type navigationToPageType;
        ViewModel.TripVoiceCommand? navigationCommand = null;
    
        // Voice command activation.
        if (args.Kind == ActivationKind.VoiceCommand) {
            // Event args may represent many different activation types. 
            // Cast the args so that you only get useful parameters out.
            var commandArgs = args as VoiceCommandActivatedEventArgs;
    
            Windows.Media.SpeechRecognition.SpeechRecognitionResult speechRecognitionResult = commandArgs.Result;
    
            // Get the name of the voice command and the text spoken.
            // See VoiceCommands.xml for supported voice commands.
            string voiceCommandName = speechRecognitionResult.RulePath[0];
            string textSpoken = speechRecognitionResult.Text;
    
            // commandMode indicates whether the command was entered using speech or text.
            // Apps should respect text mode by providing silent (text) feedback.
            string commandMode = this.SemanticInterpretation("commandMode", speechRecognitionResult);
    
            switch (voiceCommandName) {
                case "showTripToDestination":
                    // Access the value of {destination} in the voice command.
                    string destination = this.SemanticInterpretation("destination", speechRecognitionResult);
    
                    // Create a navigation command object to pass to the page.
                    navigationCommand = new ViewModel.TripVoiceCommand(
                        voiceCommandName,
                        commandMode,
                        textSpoken,
                        destination
                    );
    
                    // Set the page to navigate to for this voice command.
                    navigationToPageType = typeof(View.TripDetails);
                    break;
                default:
                    // If not able to determine what page to launch, then go to the default entry point.
                    navigationToPageType = typeof(View.TripListView);
                    break;
            }
        }
        // Protocol activation occurs when a card is selected within Cortana (using a background task).
        else if (args.Kind == ActivationKind.Protocol) {
            // Extract the launch context. In this case, use the destination from the phrase set (passed
            // along in the background task inside Cortana), which makes no attempt to be unique. A unique id or 
            // identifier is ideal for more complex scenarios. The destination page is left to check if the 
            // destination trip still exists, and navigate back to the trip list if it does not.
            var commandArgs = args as ProtocolActivatedEventArgs;
            Windows.Foundation.WwwFormUrlDecoder decoder = new Windows.Foundation.WwwFormUrlDecoder(commandArgs.Uri.Query);
            var destination = decoder.GetFirstValueByName("LaunchContext");
    
            navigationCommand = new ViewModel.TripVoiceCommand(
                "protocolLaunch",
                "text",
                "destination",
                destination
            );
    
            navigationToPageType = typeof(View.TripDetails);
        }
        else {
            // If launched using any other mechanism, fall back to the main page view.
            // Otherwise, the app will freeze at a splash screen.
            navigationToPageType = typeof(View.TripListView);
        }
    
        // Repeat the same basic initialization as OnLaunched() above, taking into account whether
        // or not the app is already active.
        Frame rootFrame = Window.Current.Content as Frame;
    
        // Do not repeat app initialization when the Window already has content,
        // just ensure that the window is active.
        if (rootFrame == null) {
            // Create a frame to act as the navigation context and navigate to the first page.
            rootFrame = new Frame();
            App.NavigationService = new NavigationService(rootFrame);
    
            rootFrame.NavigationFailed += OnNavigationFailed;
    
            // Place the frame in the current window.
            Window.Current.Content = rootFrame;
        }
    
        // Since the expectation is to always show a details page, navigate even if 
        // a content frame is in place (unlike OnLaunched).
        // Navigate to either the main trip list page, or if a valid voice command
        // was provided, to the details page for that trip.
        rootFrame.Navigate(navigationToPageType, navigationCommand);
    
        // Ensure the current window is active
        Window.Current.Activate();
    }
    
    /// <summary>
    /// Returns the semantic interpretation of a speech result. 
    /// Returns null if there is no interpretation for that key.
    /// </summary>
    /// <param name="interpretationKey">The interpretation key.</param>
    /// <param name="speechRecognitionResult">The speech recognition result to get the semantic interpretation from.</param>
    /// <returns></returns>
    private string SemanticInterpretation(string interpretationKey, SpeechRecognitionResult speechRecognitionResult) {
        return speechRecognitionResult.SemanticInterpretation.Properties[interpretationKey].FirstOrDefault();
    }
    

在应用服务中处理语音命令

在应用服务中处理语音命令。

  1. 将以下 using 指令添加到你的语音命令服务文件中。
    示例:AdventureWorksVoiceCommandService.cs

        using Windows.ApplicationModel.VoiceCommands;
        using Windows.ApplicationModel.Resources.Core;
        using Windows.ApplicationModel.AppService;
    
  2. 进行服务延迟,以便在处理语音命令时不会终止你的应用服务。

  3. 确认你的后台任务正在作为由语音命令激活的应用服务运行。

    1. IBackgroundTaskInstance.TriggerDetails 转换为 Windows.ApplicationModel.AppService.AppServiceTriggerDetails
    2. 检查 IBackgroundTaskInstance.TriggerDetails.NamePackage.appxmanifest 文件中应用服务的名称。
  4. 使用 IBackgroundTaskInstance.TriggerDetails 创建到 Cortana 的 VoiceCommandServiceConnection 以检索语音命令。

  5. VoiceCommandServiceConnection 注册一个事件处理程序。 VoiceCommandCompleted 在应用服务因用户取消而关闭时接收通知。

  6. IBackgroundTaskInstance.Canceled 注册一个事件处理程序,以便在应用服务因意外失败而关闭时接收通知。

  7. 确定命令的名称和所说的内容。

    1. 使用 VoiceCommand.CommandName 属性确定语音命令的名称。
    2. 要确定用户所说的内容,请检查 Text 的值或 SpeechRecognitionSemanticInterpretation 字典中已识别短语的语义属性。
  8. 在你的应用服务中采取适当的措施。

  9. 使用 Cortana 显示和说出对语音命令的反馈。

    1. 确定你希望 Cortana 显示并与用户对话以响应语音命令的字符串,并创建一个 VoiceCommandResponse 对象。 有关如何选择 Cortana 显示和朗读的反馈字符串的指南,请参阅 Cortana 设计指南
    2. 通过使用 VoiceCommandServiceConnection 对象调用 ReportProgressAsyncReportSuccessAsync,使用 VoiceCommandServiceConnection 实例向 Cortana 报告进度或完成情况。

    注意

    如果你需要参考你的 VCD,请访问编辑 VCD 文件部分。

    public sealed class VoiceCommandService : IBackgroundTask {
        private BackgroundTaskDeferral serviceDeferral;
        VoiceCommandServiceConnection voiceServiceConnection;
    
        public async void Run(IBackgroundTaskInstance taskInstance) {
            //Take a service deferral so the service isn&#39;t terminated.
            this.serviceDeferral = taskInstance.GetDeferral();
    
            taskInstance.Canceled += OnTaskCanceled;
    
            var triggerDetails = taskInstance.TriggerDetails as AppServiceTriggerDetails;
    
            if (triggerDetails != null &amp;&amp; 
                triggerDetails.Name == "AdventureWorksVoiceServiceEndpoint") {
                try {
                    voiceServiceConnection = 
                    VoiceCommandServiceConnection.FromAppServiceTriggerDetails(
                        triggerDetails);
                    voiceServiceConnection.VoiceCommandCompleted += 
                    VoiceCommandCompleted;
    
                    VoiceCommand voiceCommand = await 
                    voiceServiceConnection.GetVoiceCommandAsync();
    
                    switch (voiceCommand.CommandName) {
                        case "whenIsTripToDestination":
                            {
                                var destination = 
                                voiceCommand.Properties["destination"][0];
                                SendCompletionMessageForDestination(destination);
                                break;
                            }
    
                            // As a last resort, launch the app in the foreground.
                        default:
                            LaunchAppInForeground();
                            break;
                    }
                }
                finally {
                    if (this.serviceDeferral != null) {
                        // Complete the service deferral.
                        this.serviceDeferral.Complete();
                    }
                }
            }
        }
    
        private void VoiceCommandCompleted(VoiceCommandServiceConnection sender,
            VoiceCommandCompletedEventArgs args) {
            if (this.serviceDeferral != null) {
                // Insert your code here.
                // Complete the service deferral.
                this.serviceDeferral.Complete();
            }
        }
    
        private async void SendCompletionMessageForDestination(
            string destination) {
            // Take action and determine when the next trip to destination
            // Insert code here.
    
            // Replace the hardcoded strings used here with strings 
            // appropriate for your application.
    
            // First, create the VoiceCommandUserMessage with the strings 
            // that Cortana will show and speak.
            var userMessage = new VoiceCommandUserMessage();
            userMessage.DisplayMessage = "Here's your trip.";
            userMessage.SpokenMessage = "Your trip to Vegas is on August 3rd.";
    
            // Optionally, present visual information about the answer.
            // For this example, create a VoiceCommandContentTile with an 
            // icon and a string.
            var destinationsContentTiles = new List<VoiceCommandContentTile>();
    
            var destinationTile = new VoiceCommandContentTile();
            destinationTile.ContentTileType = 
                VoiceCommandContentTileType.TitleWith68x68IconAndText;
            // The user taps on the visual content to launch the app. 
            // Pass in a launch argument to enable the app to deep link to a 
            // page relevant to the item displayed on the content tile.
            destinationTile.AppLaunchArgument = 
                string.Format("destination={0}", "Las Vegas");
            destinationTile.Title = "Las Vegas";
            destinationTile.TextLine1 = "August 3rd 2015";
            destinationsContentTiles.Add(destinationTile);
    
            // Create the VoiceCommandResponse from the userMessage and list    
            // of content tiles.
            var response = VoiceCommandResponse.CreateResponse(
                userMessage, destinationsContentTiles);
    
            // Cortana displays a "Go to app_name" link that the user 
            // taps to launch the app. 
            // Pass in a launch to enable the app to deep link to a page 
            // relevant to the voice command.
            response.AppLaunchArgument = string.Format(
                "destination={0}", "Las Vegas");
    
            // Ask Cortana to display the user message and content tile and 
            // also speak the user message.
            await voiceServiceConnection.ReportSuccessAsync(response);
        }
    
        private async void LaunchAppInForeground() {
            var userMessage = new VoiceCommandUserMessage();
            userMessage.SpokenMessage = "Launching Adventure Works";
    
            var response = VoiceCommandResponse.CreateResponse(userMessage);
    
            // When launching the app in the foreground, pass an app 
            // specific launch parameter to indicate what page to show.
            response.AppLaunchArgument = "showAllTrips=true";
    
            await voiceServiceConnection.RequestAppLaunchAsync(response);
        }
    }
    

激活后,应用服务有 0.5 秒的时间调用 ReportSuccessAsync Cortana 显示并说出一个反馈字符串。

注意

你可以在 VCD 文件中声明一个反馈字符串。 该字符串不会影响 Cortana 画布上显示的 UI 文本,它只会影响 Cortana 说出的文本。

如果应用拨打电话的时间超过 0.5 秒,Cortana 会插入一个切换屏幕,如此处所示。 Cortana 会显示切换屏幕,直到应用程序调用 ReportSuccessAsync 或最多 5 秒。 如果应用服务未调用 ReportSuccessAsync 或任何向 Cortana 提供信息的 VoiceCommandServiceConnection 方法,则用户会收到错误消息并取消应用服务。

在后台使用 AdventureWorks 应用的 Cortana 和包含进度和结果屏幕的基本查询的屏幕截图