Collegamento diretto da un'app in background in Cortana a un'app in primo piano

Avviso

Questa funzionalità non è più supportata a partire dall'aggiornamento di Windows 10 di maggio 2020 (versione 2004, nome codice "20H1").

Per informazioni sul modo in cui Cortana trasforma le esperienze di produttività moderne, vedere Cortana in Microsoft 365.

Fornire collegamenti diretti da un'app in background in Cortana che avvia l'app in primo piano in un contesto o stato specifico.

Nota

Cortana e il servizio dell'app in background vengono entrambi terminati all'avvio dell'app in primo piano.

Un collegamento diretto viene visualizzato per impostazione predefinita nella schermata di completamento di Cortana, come illustrato qui ("Vai ad AdventureWorks"), ma è possibile visualizzare collegamenti diretti su altre schermate.

Screenshot del completamento dell'app in background di Cortana per un prossimo viaggio

Panoramica

Gli utenti possono accedere all'app tramite Cortana:

Qui si illustra il deep linking.

Il deep linking è utile quando Cortana e il servizio app fungono da gateway per l'app completa (anziché richiedere all'utente di avviare l'app tramite il menu Start) o per fornire l'accesso a dettagli e funzionalità più avanzati all'interno dell'app che Cortana non fornisce. Il deep linking è un altro modo per aumentare l'usabilità e promuovere l'app.

Esistono tre modi per fornire collegamenti diretti:

  • Un collegamento "Vai all'<app>" nelle varie schermate di Cortana.
  • Un collegamento incorporato in un riquadro di contenuti su varie schermate di Cortana.
  • Avvio dell'app in primo piano a livello di codice dal servizio app in background.

Cortana visualizza "Vai all'<app>" sotto la scheda contenuto nella maggior parte delle schermate.

Screenshot del collegamento diretto

È possibile fornire un argomento di avvio per questo collegamento che apre l'app in un contesto simile a quello del servizio app. Se non si fornisce un argomento di avvio, l'app viene avviata nella schermata principale.

In questo esempio tratto da AdventureWorksVoiceCommandService.cs dell'esempio AdventureWorks, si passa la stringa di destinazione specificata (destination) al metodo SendCompletionMessageForDestination, che recupera tutti i viaggi corrispondenti e fornisce un collegamento diretto all'app.

Innanzitutto, si crea un VoiceCommandUserMessage (userMessage) pronunciato da Cortana e mostrato nell'area di disegno di Cortana. Si crea quindi un oggetto elenco VoiceCommandContentTile per visualizzare la raccolta di schede risultato nell'area di disegno.

Questi due oggetti vengono quindi passati al metodo CreateResponse dell'oggetto VoiceCommandResponse (response). Si imposta quindi il valore della proprietà AppLaunchArgument dell'oggetto risposta sul valore di destination passato a questa funzione. Quando un utente tocca un riquadro di contenuto nell'area di disegno di Cortana, i valori dei parametri vengono passati all'app tramite l'oggetto risposta.

Infine, si chiama il metodo ReportSuccessAsync di VoiceCommandServiceConnection.

/// <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);
}

È possibile aggiungere collegamenti diretti alle schede di contenuto in varie schermate di Cortana .

Screenshot del canvas di Cortana per il flusso dell'app in background cortana end-to-end con il prossimo viaggio di AdventureWorks con handoff"Prossimo viaggio" di AdventureWorks con schermata di handoff

Analogamente ai collegamenti "Vai all'<app>", è possibile fornire un argomento di avvio per aprire l'app con un contesto simile a quello del servizio app. Se non si fornisce un argomento di avvio, il riquadro di contenuto non si collega all'app.

In questo esempio tratto da AdventureWorksVoiceCommandService.cs dell'esempio AdventureWorks, si passa la destinazione specificata al metodo SendCompletionMessageForDestination, che recupera tutti i viaggi corrispondenti e fornisce schede di contenuto con collegamenti diretti all'app.

Innanzitutto, si crea un VoiceCommandUserMessage (userMessage) pronunciato da Cortana e mostrato nell'area di disegno di Cortana. Si crea quindi un oggetto elenco VoiceCommandContentTile per visualizzare la raccolta di schede risultato nell'area di disegno.

Questi due oggetti vengono quindi passati al metodo CreateResponse dell'oggetto VoiceCommandResponse (response). Si imposta quindi il valore della proprietà AppLaunchArgument sul valore della destinazione nel comando vocale.

Infine, si chiama il metodo ReportSuccessAsync di VoiceCommandServiceConnection. Qui si aggiungono due riquadri di contenuto con valori dei parametri AppLaunchArgument diversi su un elenco VoiceCommandContentTile usato nella chiamata a ReportSuccessAsync dell'oggetto VoiceCommandServiceConnection.

/// <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);
}

È anche possibile avviare l'app a livello di codice con un argomento di avvio per aprire l'app con un contesto simile a quello del servizio app. Se non si fornisce un argomento di avvio, l'app viene avviata nella schermata principale.

Qui si aggiunge un parametro AppLaunchArgument con il valore "Las Vegas" su un oggetto VoiceCommandResponse usato nella chiamata a RequestAppLaunchAsync dell'oggetto VoiceCommandServiceConnection.

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);

Manifesto dell'app

Per abilitare il deep linking all'app, è necessario dichiarare l'estensione windows.personalAssistantLaunch nel file Package.appxmanifest del progetto app.

Qui, si dichiara l'estensione windows.personalAssistantLaunch per l'app Adventure Works.

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

Contratto protocollo

L'app viene avviata in primo piano tramite l'attivazione dell'URI (Uniform Resource Identifier) usando un contratto protocollo. L'app deve eseguire l'evento OnActivated dell'app e cercare un elemento ActivationKind per protocollo. Per altre informazioni, vedere Gestire l'attivazione dell'URI.

Qui, si decodifica l'URI fornito da ProtocolActivatedEventArgs per accedere all'argomento di avvio. Per questo esempio, l'URI è impostato su "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();
  }