次の方法で共有


Cortana のバックグラウンド アプリからフォアグラウンド アプリへのディープ リンク

警告

この機能は、Windows 10 May 2020 Update (バージョン 2004、コードネーム "20H1") ではサポートされなくなりました。

Cortana が最新の生産性エクスペリエンスをどのように変革させているかについては、「Microsoft 365 の Cortana」を参照してください。

Cortana でバックグラウンド アプリからのディープ リンクを提供し、フォアグラウンドに特定の状態やコンテキストでアプリを起動します。

Note

フォアグラウンド アプリが起動されると、Cortana とバックグラウンド アプリ サービスの両方が終了します。

ここに示すように、Cortana の完了画面にはディープ リンクが既定で表示されますが ("AdventureWorks に移動")、他のさまざまな画面にディープ リンクを表示できます。

今後の旅行の Cortana バックグラウンド アプリの完了のスクリーンショット

概要

ユーザーは、次の方法で Cortana からアプリにアクセスできます。

ここでは、ディープ リンクについて説明します。

ディープ リンクは、Cortana と App Service がフル機能のアプリへのゲートウェイとして機能する場合 (ユーザーがスタート メニューを介してアプリを起動する必要がない場合)、または Cortana では不可能なアプリ内のより詳細な機能へのアクセスを提供する場合に便利です。 ディープ リンクは、使いやすさを高め、アプリを昇格させるもう 1 つの方法です。

ディープ リンクを提供するには、次の 3 つの方法があります。

  • さまざまな Cortana スクリーンの "<アプリ> に移動" リンク。
  • さまざまな Cortana スクリーンのコンテンツ タイルに埋め込まれたリンク。
  • バックグラウンド アプリ サービスからプログラムによってフォアグラウンド アプリを起動します。

Cortana では、ほとんどの画面のコンテンツ カードの下に "<アプリ> に移動" ディープ リンクが表示されます。

バックグラウンド アプリの完了画面の Cortana の [アプリに移動] ディープ リンクのスクリーンショット。

このリンクの起動引数を指定し、アプリ サービスと同様のコンテキストでアプリを開くことができます。 起動引数を指定しない場合、アプリはメイン画面に起動されます。

AdventureWorks サンプルの AdventureWorksVoiceCommandService.cs のこの例では、指定された宛先 (destination) 文字列を SendCompletionMessageForDestination メソッドに渡します。このメソッドは、一致するすべての乗車を取得し、アプリへのディープ リンクを提供します。

最初に、Cortana に話させ、Cortana キャンバスに表示する VoiceCommandUserMessage (userMessage) を作成します。 その後、結果カードのコレクションをキャンバスに表示するために、VoiceCommandContentTile リスト オブジェクトが作成されます。

これら 2 つのオブジェクトは、VoiceCommandResponse オブジェクト (response) の CreateResponse メソッドに渡されます。 次に、応答オブジェクトの AppLaunchArgument プロパティ値を、この関数に渡される destination の値に設定します。 ユーザーが Cortana キャンバスでコンテンツ タイルをタップすると、パラメーター値が応答オブジェクトを介してアプリに渡されます。

最後に、VoiceCommandServiceConnectionReportSuccessAsync メソッドを呼び出します。

/// <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 specified in the voice command.</param>
private async Task SendCompletionMessageForDestination(string destination)
{
...
	IEnumerable<Model.Trip> trips = store.Trips.Where(p => p.Destination == destination);

	var userMessage = new VoiceCommandUserMessage();
	var destinationsContentTiles = new List<VoiceCommandContentTile>();
...
	var response = VoiceCommandResponse.CreateResponse(userMessage, destinationsContentTiles);

	if (trips.Count() > 0)
	{
		response.AppLaunchArgument = destination;
	}

	await voiceServiceConnection.ReportSuccessAsync(response);
}

さまざまな Cortana 画面でコンテンツ カードへのディープ リンクを追加できます。

引き渡しで AdventureWorks の今後の旅行を使用した Cortana バックグラウンド アプリ フローのエンド ツー エンドの Cortana キャンバスのスクリーンショットハンドオフ スクリーンが表示された AdventureWorks の "今後の旅行"

"<アプリ> に移動" リンクと同様に、起動引数を指定して、アプリ サービスと同様のコンテキストでアプリを開くことができます。 起動引数を指定しない場合、コンテンツ タイルはアプリにリンクされません。

AdventureWorks サンプルの AdventureWorksVoiceCommandService.cs のこの例では、指定した宛先を SendCompletionMessageForDestination メソッドに渡します。このメソッドは、一致するすべての乗車を取得し、アプリへのディープ リンクを含むコンテンツ カードを提供します。

最初に、Cortana に話させ、Cortana キャンバスに表示する VoiceCommandUserMessage (userMessage) を作成します。 その後、結果カードのコレクションをキャンバスに表示するために、VoiceCommandContentTile リスト オブジェクトが作成されます。

これら 2 つのオブジェクトは、VoiceCommandResponse オブジェクト (response) の CreateResponse メソッドに渡されます。 次に、AppLaunchArgument プロパティの値を音声コマンドの宛先の値に設定します。

最後に、VoiceCommandServiceConnectionReportSuccessAsync メソッドを呼び出します。 ここでは、AppLaunchArgument パラメーター値が異なる 2 つのコンテンツ タイルを、VoiceCommandServiceConnection オブジェクトの ReportSuccessAsync 呼び出しで使用される VoiceCommandContentTile リストに追加します。

/// <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 specified in the voice command.</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);
}

また、起動引数を使用してプログラムでアプリを起動し、アプリ サービスと同様のコンテキストでアプリを開くこともできます。 起動引数を指定しない場合、アプリはメイン画面に起動されます。

ここでは、VoiceCommandServiceConnection オブジェクトの RequestAppLaunchAsync 呼び出しで使用される VoiceCommandResponse オブジェクトに、値が "Las Vegas" の AppLaunchArgument パラメーターを追加します。

var userMessage = new VoiceCommandUserMessage();
userMessage.DisplayMessage = "Here are your trips.";
userMessage.SpokenMessage = 
  "You have one trip to Vegas coming up.";

response = VoiceCommandResponse.CreateResponse(userMessage);
response.AppLaunchArgument = “Las Vegas”;
await  VoiceCommandServiceConnection.RequestAppLaunchAsync(response);

アプリ マニフェスト

アプリへのディープ リンクを有効にするには、アプリ プロジェクトの Package.appxmanifest ファイルで windows.personalAssistantLaunch 拡張機能を宣言する必要があります。

ここでは、Adventure Works アプリの windows.personalAssistantLaunch 拡張機能を宣言します。

<Extensions>
  <uap:Extension Category="windows.appService" 
    EntryPoint="AdventureWorks.VoiceCommands.AdventureWorksVoiceCommandService">
    <uap:AppService Name="AdventureWorksVoiceCommandService"/>
  </uap:Extension>
  <uap:Extension Category="windows.personalAssistantLaunch"/> 
</Extensions>

プロトコル コントラクト

プロトコル コントラクトを使用して、Uri (Uniform Resource Identifier) アクティブ化によってアプリがフォアグラウンドで起動されます。 アプリは、ActivationKind of Protocol のアプリの OnActivated イベントとチェックをオーバーライドする必要があります。 詳しくは、「URI のアクティブ化の処理」をご覧ください。

ここでは、ProtocolActivatedEventArgs によって提供される URI をデコードして、起動引数にアクセスします。 この例では、Uri は "windows.personalassistantlaunch:?LaunchContext=Las Vegas" に設定されています。

if (args.Kind == ActivationKind.Protocol)
  {
    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);

    rootFrame.Navigate(navigationToPageType, navigationCommand);

    // Ensure the current window is active.
    Window.Current.Activate();
  }