다음을 통해 공유


Cortana를 통해 음성 명령으로 백그라운드 앱 활성화

Warning

이 기능은 Windows 10 2020년 5월 업데이트(버전 2004, 코드명 “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가 백그라운드에서 호출하는 App Service(Windows.ApplicationModel.AppService 참조)를 만듭니다.
  2. VCD 파일을 만듭니다. VCD 파일은 사용자가 앱을 활성화할 때 작업을 시작하거나 명령을 호출하기 위해 말할 수 있는 모든 음성 명령을 정의하는 XML 문서입니다. VCD 요소 및 특성 v1.2를 참조하세요.
  3. 앱이 실행될 때 VCD 파일에 명령 집합을 등록합니다.
  4. App Service의 백그라운드 활성화 및 음성 명령 실행을 처리합니다.
  5. Cortana 내에서 음성 명령에 적절한 피드백을 표시하고 말합니다.

필수 구성 요소

UWP(유니버설 Windows 플랫폼) 앱을 처음 개발하는 경우 다음 항목을 살펴보고 여기서 설명하는 기술을 숙지하세요.

사용자 환경 참고 자료

앱을 Cortana와 통합하는 방법에 대한 정보는 Cortana 디자인 가이드라인을 참조하고 유용하고 매력적인 음성 지원 앱을 디자인하는 데 유용한 팁은 음성 상호 작용을 참조하세요.

Visual Studio에서 기본 프로젝트로 새 솔루션 만들기

  1. Microsoft Visual Studio 2015를 시작합니다.
    Visual Studio 2015 시작 페이지가 나타납니다.

  2. 파일 메뉴에서 >프로젝트를 선택합니다.
    새 프로젝트 대화 상자가 나타납니다. 대화 상자의 왼쪽 창에서 표시할 템플릿 유형을 선택할 수 있습니다.

  3. 왼쪽 창에서 설치된 > 템플릿 > Visual C# > Windows를 확장한 다음, 유니버설 템플릿 그룹을 선택합니다. 대화 상자의 가운데 창에는 UWP(유니버설 Windows 플랫폼) 앱용 프로젝트 템플릿 목록이 표시됩니다.

  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)를 참조하세요.

현재 지역화되거나 다중 해상도 리소스를 제공할 계획이 없더라도 문자열 리소스 파일(예: en-US\resources.resw)의 기본 언어와 이미지의 기본 배율(예: logo.scale-100.png)을 표시하는 것이 좋습니다. 그러나 최소한 100, 200 및 400 배율 인수에 대한 자산을 제공하는 것이 좋습니다.

Important

Cortana 캔버스의 제목 영역에 사용되는 앱 아이콘은 Package.appxmanifest 파일에 지정된 Square44x44Logo 아이콘입니다.
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")
);  

App Service 프로젝트 만들기

  1. 솔루션 이름을 마우스 오른쪽 단추로 클릭하고 새로 만들기 > 프로젝트를 선택합니다.

  2. 설치됨 > 템플릿 > Visual C# > Windows > 유니버설에서 Windows 런타임 구성 요소를 선택합니다. Windows 런타임 구성 요소는 App Service(Windows.ApplicationModel.AppService)를 구현하는 구성 요소입니다.

  3. 프로젝트 이름을 입력하고 확인 단추를 클릭합니다.
    예: VoiceCommandService

  4. 솔루션 탐색기에서 VoiceCommandService 프로젝트를 선택하고 Visual Studio에서 생성한 Class1.cs 파일의 이름을 바꿉니다. 예: Adventure WorksAdventureWorksVoiceCommandService.cs를 사용합니다.

  5. 단추를 클릭합니다. 모든 Class1.cs 항목의 이름을 바꿀 것인지 묻는 메시지가 표시됩니다.

  6. AdventureWorksVoiceCommandService.cs 파일에서:

    1. 다음 using 지시문을 추가합니다.
      using Windows.ApplicationModel.Background;
    2. 새 프로젝트를 만들 때 프로젝트 이름은 모든 파일의 기본 루트 네임스페이스로 사용됩니다. 기본 프로젝트 아래에 App Service 코드를 중첩하도록 네임스페이스의 이름을 바꿉니다. 예: namespace AdventureWorks.VoiceCommands
    3. 솔루션 탐색기에서 App Service 프로젝트 이름을 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.
    4. 라이브러리 탭에서 동일한 값으로 기본 네임스페이스 필드를 업데이트합니다.
      예: AdventureWorks.VoiceCommands).
    5. IBackgroundTask 인터페이스를 구현하는 새 클래스를 만듭니다. 이 클래스에는 Cortana가 음성 명령을 인식할 때 진입점인 Run 메서드가 필요합니다.

    예: 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. Application 요소에 Extensions 요소를 추가합니다.
    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 특성의 값을 App Service의 이름(이 경우 AdventureWorksVoiceCommandService)으로 설정합니다.
    9. Extensions 요소에 두 번째 uap:Extension 요소를 추가합니다.
    10. uap:Extension 요소에 Category 특성을 추가하고 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. 이 App Service 프로젝트를 기본 프로젝트의 참조로 추가합니다.

    1. 참조를 마우스 오른쪽 단추로 클릭합니다.
    2. 참조 추가...를 선택합니다.
    3. 참조 관리자 대화 상자에서 프로젝트를 확장하고 App Service 프로젝트를 선택합니다.
    4. 확인 단추를 클릭합니다.

VCD 파일 만들기

  1. Visual Studio에서 기본 프로젝트 이름을 마우스 오른쪽 단추로 클릭하고 > 새 항목 추가를 선택합니다. XML 파일을 추가합니다.
  2. VCD 파일의 이름을 입력합니다.
    예: AdventureWorksCommands.xml
  3. 추가 단추를 클릭합니다.
  4. 솔루션 탐색기에서 VCD 파일을 선택합니다.
  5. 속성 창에서 작업 빌드콘텐츠로 설정한 다음, 출력 디렉터리에 복사최신 버전인 경우 복사로 설정합니다.

VCD 파일 편집

  1. https://schemas.microsoft.com/voicecommands/1.2를 가리키는 xmlns 특성이 있는 VoiceCommands 요소를 추가합니다.

  2. 앱에서 지원하는 각 언어에 대해 앱에서 지원하는 음성 명령을 포함하는 CommandSet 요소를 만듭니다.
    각기 다른 xml:lang 특성을 가진 여러 CommandSet 요소를 선언할 수 있으므로 앱이 다른 시장에서 사용될 수 있습니다. 예를 들어 미국 앱에는 영어를 위해 CommandSet와 스페인어를 위해 CommandSet가 있을 수 있습니다.

    Important

    앱을 활성화하고 음성 명령을 사용하여 작업을 시작하려면 앱이 사용자의 디바이스에 표시된 음성 언어와 일치하는 언어로 CommandSet 요소를 포함하는 VCD 파일을 등록해야 합니다. 음성 언어는 설정 > 시스템 > 음성 > 음성 언어에 있습니다.

  3. 지원하려는 각 명령에 대해 Command 요소를 추가합니다.
    VCD 파일에 선언된 각 Command에는 다음 정보가 포함되어야 합니다.

    • 애플리케이션이 런타임에 음성 명령을 식별하는 데 사용하는 Name 특성입니다.

    • 사용자가 명령을 호출하는 방법을 설명하는 구가 포함된 Example 요소입니다. Cortana는 사용자가 What can I say?, Help라고 말하거나 더보기를 탭할 때의 예를 보여 줍니다.

    • 앱이 명령으로 인식하는 단어 또는 구를 포함하는 ListenFor 요소입니다. 각 ListenFor 요소는 명령과 관련된 특정 단어를 포함하는 하나 이상의 PhraseList 요소에 대한 참조를 포함할 수 있습니다.

      참고 항목

      ListenFor 요소는 프로그래밍 방식으로 수정하면 안 됩니다. 그러나 ListenFor 요소와 연결된 PhraseList 요소는 프로그래밍 방식으로 수정할 수 있습니다. 애플리케이션은 사용자가 앱을 사용할 때 생성된 데이터 세트를 기반으로 런타임 시 PhraseList 요소의 콘텐츠를 수정해야 합니다.

      자세한 내용은 Cortana VCD 구 목록을 동적으로 수정을 참조하세요.

    • 애플리케이션이 시작될 때 표시하고 말할 Cortana에 대한 텍스트를 포함하는 Feedback 요소입니다.

Navigate 요소는 음성 명령이 앱을 포그라운드로 활성화함을 나타냅니다. 이 예에서 showTripToDestination 명령은 포그라운드 작업입니다.

VoiceCommandService 요소는 음성 명령이 백그라운드에서 앱을 활성화함을 나타냅니다. 이 요소의 Target 특성 값은 package.appxmanifest 파일에 있는 uap:AppService 요소의 Name 특성 값과 일치해야 합니다. 이 예에서 whenIsTripToDestinationcancelTripToDestination 명령은 App Service의 이름을 AdventureWorksVoiceCommandService으로 지정하는 백그라운드 작업입니다.

자세한 내용은 VCD 요소 및 특성 v1.2 참조를 확인합니다.

예: Adventure Works 앱의 en-us 음성 명령을 정의하는 VCD 파일의 일부입니다.

<?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 속성을 쿼리합니다.

    사용자가 말한 내용을 확인하려면 텍스트의 값이나 SpeechRecognitionSemanticInterpretation 사전에서 인식된 구의 의미 체계 속성을 확인합니다.

  3. 원하는 페이지로 이동하는 등 앱에서 적절한 작업을 수행합니다.

    참고 항목

    VCD를 참조해야 하는 경우 VCD 파일 편집 섹션을 방문합니다.

    음성 명령에 대한 음성 인식 결과를 수신한 후 RulePath 배열의 첫 번째 값에서 명령 이름을 가져옵니다. VCD 파일은 하나 이상의 가능한 음성 명령을 정의하므로 값이 VCD의 명령 이름과 일치하는지 확인하고 적절한 작업을 취해야 합니다.

    애플리케이션에 대한 가장 일반적인 작업은 음성 명령 컨텍스트와 관련된 콘텐츠가 있는 페이지로 이동하는 것입니다.
    예: TripPage 페이지를 열고 음성 명령 값, 명령 입력 방법 및 인식된 대상 구(해당되는 경우)를 전달합니다. 또는 TripPage 페이지로 이동할 때 앱이 SpeechRecognitionResult에 탐색 매개 변수를 보낼 수 있습니다.

    commandMode 키를 사용하여 SpeechRecognitionSemanticInterpretation.Properties 사전에서 앱을 실행한 음성 명령이 실제로 음성인지 또는 텍스트로 입력되었는지 확인할 수 있습니다. 해당 키의 값은 voice 또는 text입니다. 키 값이 voice이면 앱에서 음성 합성(Windows.Media.SpeechSynthesis)을 사용하여 사용자에게 음성 피드백을 제공하는 것이 좋습니다.

    SpeechRecognitionSemanticInterpretation.Properties를 사용하여 ListenFor 요소의 PhraseList 또는 PhraseTopic 제약 조건에서 말한 콘텐츠를 찾습니다. 사전 키는 PhraseList 또는 PhraseTopic 요소의 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();
    }
    

App Service에서 음성 명령 처리

App Service에서 음성 명령을 처리합니다.

  1. 음성 명령 서비스 파일에 다음 using 지시문을 추가합니다.
    예: AdventureWorksVoiceCommandService.cs

        using Windows.ApplicationModel.VoiceCommands;
        using Windows.ApplicationModel.Resources.Core;
        using Windows.ApplicationModel.AppService;
    
  2. 음성 명령을 처리하는 동안 App Service가 종료되지 않도록 서비스 지연을 수행합니다.

  3. 백그라운드 작업이 음성 명령으로 활성화된 App Service로 실행되고 있는지 확인합니다.

    1. IBackgroundTaskInstance.TriggerDetailsWindows.ApplicationModel.AppService.AppServiceTriggerDetails로 캐스팅합니다.
    2. IBackgroundTaskInstance.TriggerDetails.NamePackage.appxmanifest 파일의 App Service 이름인지 확인합니다.
  4. IBackgroundTaskInstance.TriggerDetails를 통해 Cortana에 대한 VoiceCommandServiceConnection을 만들어 음성 명령을 검색합니다.

  5. VoiceCommandServiceConnection에 대한 이벤트 처리기를 등록합니다. VoiceCommandCompleted를 사용하여 사용자 취소로 인해 App Service가 종료될 때 알림을 받습니다.

  6. IBackgroundTaskInstance.Canceled에 대한 이벤트 처리기를 등록하여 예기치 않은 오류로 인해 App Service가 종료될 때 알림을 수신합니다.

  7. 명령의 이름과 말한 내용을 확인합니다.

    1. VoiceCommand.CommandName 속성을 사용하여 음성 명령의 이름을 확인합니다.
    2. 사용자가 말한 내용을 확인하려면 텍스트의 값이나 SpeechRecognitionSemanticInterpretation 사전에서 인식된 구의 의미 체계 속성을 확인합니다.
  8. App Service에서 적절한 작업을 수행합니다.

  9. Cortana를 사용하여 음성 명령에 대한 피드백을 표시하고 말합니다.

    1. 음성 명령에 대한 응답으로 Cortana가 사용자에게 표시하고 말할 문자열을 결정하고 VoiceCommandResponse 개체를 만듭니다. Cortana가 표시하고 말하는 피드백 문자열을 선택하는 방법에 대한 지침은 Cortana 디자인 지침을 참조하세요.
    2. VoiceCommandServiceConnection 인스턴스를 통해 VoiceCommandServiceConnection 개체로 ReportProgressAsync 또는 ReportSuccessAsync를 호출하여 진행 상황 또는 완료를 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);
        }
    }
    

활성화되면 App Service에서 0.5초 이내에 ReportSuccessAsync를 호출할 수 있습니다. Cortana는 피드백 문자열을 표시하고 말합니다.

참고 항목

VCD 파일에서 피드백 문자열을 선언할 수 있습니다. 문자열은 Cortana 캔버스에 표시되는 UI 텍스트에 영향을 미치지 않으며 Cortana에서 말하는 텍스트에만 영향을 줍니다.

앱이 전화를 거는 데 0.5초 이상 걸리면 여기에 표시된 것처럼 Cortana가 전달 화면을 삽입합니다. Cortana는 애플리케이션이 ReportSuccessAsync를 호출할 때까지 또는 최대 5초 동안 전달 화면을 표시합니다. App Service가 ReportSuccessAsync 또는 Cortana에 정보를 제공하는 VoiceCommandServiceConnection 메서드를 호출하지 않는 경우 사용자는 오류 메시지를 받고 App Service가 취소됩니다.

백그라운드에서 AdventureWorks 앱을 사용하여 진행률 및 결과 화면이 있는 Cortana 및 기본 쿼리 스크린샷