警告
この機能は、Windows 10 May 2020 Update (バージョン 2004、コードネーム "20H1") ではサポートされなくなりました。
音声コマンドの実行中に、 Cortana キャンバスで音声とテキスト入力を使用して、バックグラウンド アプリとのユーザー操作を有効にします。
Cortana は、アプリを使用したターンバイターン ワークフロー全体をサポートしています。 このワークフローはアプリによって定義され、次のような機能をサポートできます。
- 正常に完了しました
- ハンドオフ
- 進行状況
- 確認
- 曖昧性の除去
- エラー
フィードバック文字列の作成
ヒント
必要条件
ユニバーサル Windows プラットフォーム (UWP) アプリを開発するのが初めての場合は、以下のトピックに目を通して、ここで説明されているテクノロジをよく理解できるようにしてください。
- 初めてのアプリの作成
- 「イベントとルーティング イベントの概要」に記載されているイベントの説明
ユーザー エクスペリエンス ガイドライン
アプリと Cortana を統合する方法については「Cortana の設計ガイドライン」を、便利で魅力的な音声認識対応アプリの設計に役立つヒントについては「音声操作」を参照してください。
Cortana によって表示および読み上げられたフィードバック文字列を作成します。
「Cortana の設計ガイドライン」では、Cortana の文字列の作成に関する推奨事項を提供しています。
コンテンツ カードは、ユーザーに追加のコンテキストを提供し、フィードバック文字列を簡潔に保つのに役立ちます。
Cortana では、次のコンテンツ カード テンプレートがサポートされています (完了画面で使用できるテンプレートは 1 つだけです)。
- タイトルのみ
- 最大 3 行のテキストを含むタイトル
- 画像を含むタイトル
- 画像と最大 3 行のテキストを含むタイトル
イメージは次のようになります。
- 幅68cm x 高さ68cm
- 幅68cm x 高さ92cm
- 幅280、高さ140
カードまたはアプリへのテキスト リンクをクリックして、ユーザーがフォアグラウンドでアプリを起動できるようにすることもできます。
完了画面
完了画面は、完了した音声コマンド タスクに関する情報をユーザーに提供します。
アプリの応答に 500 ミリ秒未満しかかからず、ユーザーからの追加情報を必要としないタスクは、それ以上の Cortana とのやり取りなしで完了します。 Cortana は、完了画面を表示するだけです。
ここでは、 Adventure Works アプリを使用して、ロンドンへの今後の旅行を表示する音声コマンド要求の完了画面を表示します。
音声コマンドは、AdventureWorksCommands.xmlで定義されます。
<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>
AdventureWorksVoiceCommandService.csには、完了メッセージ メソッドが含まれています。
/// <summary>
/// Show details for a single trip, if the trip can be found.
/// This demonstrates a simple response flow in Cortana.
/// </summary>
/// <param name="destination">The destination, expected to be in the phrase list.</param>
private async Task SendCompletionMessageForDestination(string destination)
{
// If this operation is expected to take longer than 0.5 seconds, the task must
// supply a progress response to Cortana before starting the operation, and
// updates must be provided at least every 5 seconds.
string loadingTripToDestination = string.Format(
cortanaResourceMap.GetValue("LoadingTripToDestination", cortanaContext).ValueAsString,
destination);
await ShowProgressScreen(loadingTripToDestination);
Model.TripStore store = new Model.TripStore();
await store.LoadTrips();
// Query for the specified trip.
// The destination should be in the phrase list. However, there might be
// multiple trips to the destination. We pick the first.
IEnumerable<Model.Trip> trips = store.Trips.Where(p => p.Destination == destination);
var userMessage = new VoiceCommandUserMessage();
var destinationsContentTiles = new List<VoiceCommandContentTile>();
if (trips.Count() == 0)
{
string foundNoTripToDestination = string.Format(
cortanaResourceMap.GetValue("FoundNoTripToDestination", cortanaContext).ValueAsString,
destination);
userMessage.DisplayMessage = foundNoTripToDestination;
userMessage.SpokenMessage = foundNoTripToDestination;
}
else
{
// Set plural or singular title.
string message = "";
if (trips.Count() > 1)
{
message = cortanaResourceMap.GetValue("PluralUpcomingTrips", cortanaContext).ValueAsString;
}
else
{
message = cortanaResourceMap.GetValue("SingularUpcomingTrip", cortanaContext).ValueAsString;
}
userMessage.DisplayMessage = message;
userMessage.SpokenMessage = message;
// Define a tile for each destination.
foreach (Model.Trip trip in trips)
{
int i = 1;
var destinationTile = new VoiceCommandContentTile();
destinationTile.ContentTileType = VoiceCommandContentTileType.TitleWith68x68IconAndText;
destinationTile.Image = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///AdventureWorks.VoiceCommands/Images/GreyTile.png"));
destinationTile.AppLaunchArgument = trip.Destination;
destinationTile.Title = trip.Destination;
if (trip.StartDate != null)
{
destinationTile.TextLine1 = trip.StartDate.Value.ToString(dateFormatInfo.LongDatePattern);
}
else
{
destinationTile.TextLine1 = trip.Destination + " " + i;
}
destinationsContentTiles.Add(destinationTile);
i++;
}
}
var response = VoiceCommandResponse.CreateResponse(userMessage, destinationsContentTiles);
if (trips.Count() > 0)
{
response.AppLaunchArgument = destination;
}
await voiceServiceConnection.ReportSuccessAsync(response);
}
ハンドオフ画面
音声コマンドが認識されると、Cortana は ReportSuccessAsync を呼び出し、フィードバックを約 500 ミリ秒以内に表示する必要があります。アプリ サービスが 500 ミリ秒以内に音声コマンドにより指定された操作を完了できない場合、Cortana はアプリが ReportSuccessAsync を呼び出すまでの間、または最大 5 秒間、ハンドオフ画面をユーザーに表示します。
アプリ サービスが ReportSuccessAsync またはその他の VoiceCommandServiceConnection メソッドを呼び出さない場合、ユーザーはエラー メッセージを受け取り、アプリ サービスの呼び出しが取り消されます。
Adventure Works アプリのハンドオフ画面の例を次に示します。 この例では、ユーザーが今後の旅行のために Cortana にクエリを実行しています。 ハンドオフ画面には、App Service 名、アイコン、および Feedback 文字列でカスタマイズされたメッセージが含まれます。
(注) フィードバック文字列は、VCD ファイル内で宣言できます。 この文字列は Cortana キャンバスに表示される UI テキストには影響せず、 Cortanaによって読み上げられたテキストにのみ影響します。
進行状況画面
App Service が ReportSuccessAsync の呼び出しに 500 ミリ秒以上かかる場合、 Cortana はユーザーに進行状況画面を提供します。 アプリ アイコンが表示され、GUI と TTS の両方の進行状況文字列を指定して、タスクがアクティブに処理されていることを示す必要があります。
Cortana には、最大 5 秒間の進行状況画面が表示されます。 5 秒後、 Cortana はユーザーにエラー メッセージを表示し、アプリ サービスが閉じられます。 アプリ サービスがアクションを完了するのに 5 秒以上必要な場合は、進行状況画面で引き続き Cortana を更新できます。
Adventure Works アプリの進行状況画面の例を次に示します。 この例では、ユーザーがラスベガスへの旅行をキャンセルしました。 進行状況画面には、アクション用にカスタマイズされたメッセージ、アイコン、キャンセル中の乗車に関する情報を含むコンテンツ タイルが含まれます。
AdventureWorksVoiceCommandService.csには、 ReportProgressAsync を呼び出して、進行状況画面を Cortana に表示する、次の進行状況メッセージ メソッドが含まれています。
/// <summary>
/// Show a progress screen. These should be posted at least every 5 seconds for a
/// long-running operation.
/// </summary>
/// <param name="message">The message to display, relating to the task being performed.</param>
/// <returns></returns>
private async Task ShowProgressScreen(string message)
{
var userProgressMessage = new VoiceCommandUserMessage();
userProgressMessage.DisplayMessage = userProgressMessage.SpokenMessage = message;
VoiceCommandResponse response = VoiceCommandResponse.CreateResponse(userProgressMessage);
await voiceServiceConnection.ReportProgressAsync(response);
}
確認画面
音声コマンドで指定されたアクションが元に戻せない、大きな影響を与える、または認識の信頼性が高くない場合、アプリ サービスは確認を要求できます。
Adventure Works アプリの確認画面の例を次に示します。 この例では、ユーザーが Cortana 経由でラスベガスへの旅行をキャンセルするようにアプリ サービスに指示しています。 アプリ サービスは、 Cortana に対して、旅行をキャンセルする前に、ユーザーに 「はい」または「いいえ」の回答を求める確認画面を提供しました。
ユーザーが "はい" または "いいえ" 以外のメッセージを表示した場合、 Cortana は質問に対する回答を特定できません。 この場合、 Cortana は、App Service によって提供される同様の質問をユーザーに求めます。
2 番目のプロンプトで、ユーザーがまだ "はい" または "いいえ" と言っていない場合、 Cortana は、同じ質問の前に説明が付いた 3 回目のプロンプトをユーザーに表示します。 ユーザーがまだ "はい" または "いいえ" と言っていない場合、 Cortana は音声入力のリッスンを停止し、代わりにいずれかのボタンをタップするようにユーザーに求めます。
確認画面には、アクション用にカスタマイズされたメッセージ、アイコン、キャンセル中の乗車に関する情報を含むコンテンツ タイルが含まれます。
AdventureWorksVoiceCommandService.csには、 RequestConfirmationAsync を呼び出して、 Cortana に確認画面を表示する、次のトリップキャンセルメソッドが含まれています。
/// <summary>
/// Handle the Trip Cancellation task. This task demonstrates how to prompt a user
/// for confirmation of an operation, show users a progress screen while performing
/// a long-running task, and show a completion screen.
/// </summary>
/// <param name="destination">The name of a destination.</param>
/// <returns></returns>
private async Task SendCompletionMessageForCancellation(string destination)
{
// Begin loading data to search for the target store.
// Consider inserting a progress screen here, in order to prevent Cortana from timing out.
string progressScreenString = string.Format(
cortanaResourceMap.GetValue("ProgressLookingForTripToDest", cortanaContext).ValueAsString,
destination);
await ShowProgressScreen(progressScreenString);
Model.TripStore store = new Model.TripStore();
await store.LoadTrips();
IEnumerable<Model.Trip> trips = store.Trips.Where(p => p.Destination == destination);
Model.Trip trip = null;
if (trips.Count() > 1)
{
// If there is more than one trip, provide a disambiguation screen.
// However, if a significant number of items are returned, you might want to
// just display a link to your app and provide a deeper search experience.
string disambiguationDestinationString = string.Format(
cortanaResourceMap.GetValue("DisambiguationWhichTripToDest", cortanaContext).ValueAsString,
destination);
string disambiguationRepeatString = cortanaResourceMap.GetValue("DisambiguationRepeat", cortanaContext).ValueAsString;
trip = await DisambiguateTrips(trips, disambiguationDestinationString, disambiguationRepeatString);
}
else
{
trip = trips.FirstOrDefault();
}
var userPrompt = new VoiceCommandUserMessage();
VoiceCommandResponse response;
if (trip == null)
{
var userMessage = new VoiceCommandUserMessage();
string noSuchTripToDestination = string.Format(
cortanaResourceMap.GetValue("NoSuchTripToDestination", cortanaContext).ValueAsString,
destination);
userMessage.DisplayMessage = userMessage.SpokenMessage = noSuchTripToDestination;
response = VoiceCommandResponse.CreateResponse(userMessage);
await voiceServiceConnection.ReportSuccessAsync(response);
}
else
{
// Prompt the user for confirmation that this is the correct trip to cancel.
string cancelTripToDestination = string.Format(
cortanaResourceMap.GetValue("CancelTripToDestination", cortanaContext).ValueAsString,
destination);
userPrompt.DisplayMessage = userPrompt.SpokenMessage = cancelTripToDestination;
var userReprompt = new VoiceCommandUserMessage();
string confirmCancelTripToDestination = string.Format(
cortanaResourceMap.GetValue("ConfirmCancelTripToDestination", cortanaContext).ValueAsString,
destination);
userReprompt.DisplayMessage = userReprompt.SpokenMessage = confirmCancelTripToDestination;
response = VoiceCommandResponse.CreateResponseForPrompt(userPrompt, userReprompt);
var voiceCommandConfirmation = await voiceServiceConnection.RequestConfirmationAsync(response);
// If RequestConfirmationAsync returns null, Cortana has likely been dismissed.
if (voiceCommandConfirmation != null)
{
if (voiceCommandConfirmation.Confirmed == true)
{
string cancellingTripToDestination = string.Format(
cortanaResourceMap.GetValue("CancellingTripToDestination", cortanaContext).ValueAsString,
destination);
await ShowProgressScreen(cancellingTripToDestination);
// Perform the operation to remove the trip from app data.
// As the background task runs within the app package of the installed app,
// we can access local files belonging to the app without issue.
await store.DeleteTrip(trip);
// Provide a completion message to the user.
var userMessage = new VoiceCommandUserMessage();
string cancelledTripToDestination = string.Format(
cortanaResourceMap.GetValue("CancelledTripToDestination", cortanaContext).ValueAsString,
destination);
userMessage.DisplayMessage = userMessage.SpokenMessage = cancelledTripToDestination;
response = VoiceCommandResponse.CreateResponse(userMessage);
await voiceServiceConnection.ReportSuccessAsync(response);
}
else
{
// Confirm no action for the user.
var userMessage = new VoiceCommandUserMessage();
string keepingTripToDestination = string.Format(
cortanaResourceMap.GetValue("KeepingTripToDestination", cortanaContext).ValueAsString,
destination);
userMessage.DisplayMessage = userMessage.SpokenMessage = keepingTripToDestination;
response = VoiceCommandResponse.CreateResponse(userMessage);
await voiceServiceConnection.ReportSuccessAsync(response);
}
}
}
}
あいまいさの解消画面
音声コマンドで指定されたアクションに複数の結果がある場合、アプリ サービスはユーザーに詳細情報を要求できます。
Adventure Works アプリのあいまいさを解消する画面の例を次に示します。 この例では、ユーザーが Cortana 経由でラスベガスへの旅行をキャンセルするようにアプリ サービスに指示しています。 ただし、ユーザーは、異なる日付にラスベガスへの 2 回の旅行を行っており、ユーザーが目的の旅行を選択しないと、アプリ サービスはアクションを完了できません。
アプリ サービスは、 Cortana を取り消す前に、一致する乗車の一覧から選択を行うようユーザーに求めるあいまいさを解消する画面を提供します。
この場合、 Cortana は、App Service によって提供される同様の質問をユーザーに求めます。
2 番目のプロンプトで、ユーザーが選択を識別するために使用できる内容をまだ言っていない場合は、 Cortana 同じ質問に対して、3 回目の質問に対して、説明が付いたメッセージが表示されます。 ユーザーが選択を識別するために使用できる何かをまだ言っていない場合は、 Cortana は音声入力のリッスンを停止し、代わりにいずれかのボタンをタップするようにユーザーに求めます。
あいまいさの解消画面には、アクション用にカスタマイズされたメッセージ、アイコン、キャンセルされる乗車に関する情報を含むコンテンツ タイルが含まれます。
AdventureWorksVoiceCommandService.csには、 RequestDisambiguationAsync を呼び出して Cortanaのあいまいさを解消する次のトリップ キャンセル メソッドが含まれています。
/// <summary>
/// Provide the user with a way to identify which trip to cancel.
/// </summary>
/// <param name="trips">The set of trips</param>
/// <param name="disambiguationMessage">The initial disambiguation message</param>
/// <param name="secondDisambiguationMessage">Repeat prompt retry message</param>
private async Task<Model.Trip> DisambiguateTrips(IEnumerable<Model.Trip> trips, string disambiguationMessage, string secondDisambiguationMessage)
{
// Create the first prompt message.
var userPrompt = new VoiceCommandUserMessage();
userPrompt.DisplayMessage =
userPrompt.SpokenMessage = disambiguationMessage;
// Create a re-prompt message if the user responds with an out-of-grammar response.
var userReprompt = new VoiceCommandUserMessage();
userReprompt.DisplayMessage =
userReprompt.SpokenMessage = secondDisambiguationMessage;
// Create card for each item.
var destinationContentTiles = new List<VoiceCommandContentTile>();
int i = 1;
foreach (Model.Trip trip in trips)
{
var destinationTile = new VoiceCommandContentTile();
destinationTile.ContentTileType = VoiceCommandContentTileType.TitleWith68x68IconAndText;
destinationTile.Image = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///AdventureWorks.VoiceCommands/Images/GreyTile.png"));
// The AppContext can be any arbitrary object.
destinationTile.AppContext = trip;
string dateFormat = "";
if (trip.StartDate != null)
{
dateFormat = trip.StartDate.Value.ToString(dateFormatInfo.LongDatePattern);
}
else
{
// The app allows a trip to have no date.
// However, the choices must be unique so they can be distinguished.
// Here, we add a number to identify them.
dateFormat = string.Format("{0}", i);
}
destinationTile.Title = trip.Destination + " " + dateFormat;
destinationTile.TextLine1 = trip.Description;
destinationContentTiles.Add(destinationTile);
i++;
}
// Cortana handles re-prompting if no valid response.
var response = VoiceCommandResponse.CreateResponseForPrompt(userPrompt, userReprompt, destinationContentTiles);
// If cortana is dismissed in this operation, null is returned.
var voiceCommandDisambiguationResult = await
voiceServiceConnection.RequestDisambiguationAsync(response);
if (voiceCommandDisambiguationResult != null)
{
return (Model.Trip)voiceCommandDisambiguationResult.SelectedItem.AppContext;
}
return null;
}
エラー画面
音声コマンドで指定されたアクションを完了できない場合、アプリ サービスはエラー画面を表示できます。
Adventure Works アプリのエラー画面の例を次に示します。 この例では、ユーザーが Cortana 経由でラスベガスへの旅行をキャンセルするようにアプリ サービスに指示しています。 ただし、ユーザーはラスベガスへの旅行をスケジュールしていません。
アプリ サービスは Cortana に、アクション用にカスタマイズされたメッセージ、アイコン、および特定のエラー メッセージを含むエラー画面を提供します。
ReportFailureAsync を呼び出して、Cortana にエラー画面を表示します。
var userMessage = new VoiceCommandUserMessage();
userMessage.DisplayMessage = userMessage.SpokenMessage =
"Sorry, you don't have any trips to Las Vegas";
var response = VoiceCommandResponse.CreateResponse(userMessage);
response.AppLaunchArgument = "showUpcomingTrips";
await voiceServiceConnection.ReportFailureAsync(response);
関連記事
Windows developer