Interactuar con una aplicación en segundo plano en Cortana

Advertencia

Esta característica ya no se admite a partir de la actualización de mayo de 2020 de Windows 10 (versión 2004, nombre de código "20H1").

Consulte Cortana en Microsoft 365 para obtener información sobre cómo Cortana está transformando las experiencias de productividad modernas.

Habilita la interacción del usuario con una aplicación en segundo plano, a través de voz y entrada de texto en el lienzo de Cortana, mientras se ejecuta un comando de voz.

Cortana es compatible con un flujo de trabajo completo paso a paso de la aplicación. Este flujo de trabajo está definido por la aplicación y puede admitir la funcionalidad, como:

  • Completado correctamente.
  • Entrega
  • Progreso
  • Confirmación
  • Anulación de ambigüedades
  • Error

Redacción de cadenas de comentarios

Sugerencia

Requisitos previos

Si acabas de empezar a desarrollar aplicaciones para la Plataforma universal de Windows (UWP), consulta estos temas para familiarizarte con las tecnologías que te presentamos aquí.

Directrices para la experiencia del usuario

Consulta Directrices de diseño de Cortana para obtener información sobre cómo integrar tu aplicación con Cortana e interacciones de voz para obtener sugerencias útiles sobre el diseño de una aplicación habilitada para voz útil y atractiva.

Componer las cadenas de comentarios, que muestra y dice Cortana.

Las directrices de diseño de Cortana proporcionan recomendaciones sobre la composición de cadenas para Cortana.

Las tarjetas de contenido pueden proporcionar contexto adicional para el usuario y ayudar a mantener las cadenas de comentarios concisas.

Cortana admite las siguientes plantillas de tarjetas de contenido (solo se puede usar una plantilla en la pantalla de finalización):

  • Solo el título
  • Título con hasta tres líneas de texto
  • Título con imagen
  • Título con imagen y hasta tres líneas de texto

La imagen puede ser:

  • 68 (ancho) x 68 (alto)
  • 68 (ancho) x 92 (alto)
  • 280 (ancho) x 140 (alto)

También puedes permitir que los usuarios inicien la aplicación en primer plano haciendo clic en una tarjeta o en el vínculo de texto a tu aplicación.

Pantalla de finalización

Una pantalla de finalización ofrece información al usuario sobre la tarea de comando de voz completada.

Las tareas que tardan menos de 500 milisegundos en responder a la aplicación y que no requieren información adicional del usuario se completan sin ninguna interacción adicional con Cortana. Cortana tan solo muestra la pantalla de finalización.

Aquí, usamos la aplicación Adventure Works para mostrar la pantalla de finalización de una solicitud de comando de voz para mostrar los próximos viajes a Londres.

Captura de pantalla de la finalización de la aplicación en segundo plano de Cortana para un próximo viaje

El comando de voz se define en 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 contiene el método de mensaje de finalización:

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

Pantalla de entrega

Una vez que se reconoce un comando de voz, Cortana debe llamar a ReportSuccessAsync y presentar comentarios en aproximadamente 500Si el servicio de aplicaciones no puede completar la acción especificada por el comando de voz en 500 ms, Cortana presenta una pantalla de entrega que se muestra hasta que la aplicación llama a ReportSuccessAsync o durante un máximo de 5 segundos.

Si el servicio de la aplicación no llama a ReportSuccessAsync o a algún otro método VoiceCommandServiceConnection, el usuario recibe un mensaje de error y se cancela la llamada del servicio de aplicaciones.

Este es un ejemplo de una pantalla de entrega de la aplicación Adventure Works. En este ejemplo, un usuario le ha consultado a Cortana los próximos viajes. La pantalla de entrega incluye un mensaje personalizado con el nombre de servicio de la aplicación, un icono y una cadena de Comentarios.

[! NOTA] Puede declarar una cadena de comentarios en el archivo VCD. Esta cadena no afecta el texto de la interfaz de usuario que se muestra en el lienzo de Cortana, solo afecta al texto que dice Cortana.

Captura de pantalla de la pantalla de entrega de la aplicación en segundo plano de Cortana

Pantalla de progreso

Si el servicio de la aplicación tarda más de 500 ms en llamar a ReportSuccessAsync, Cortana muestra al usuario una pantalla de progreso. A continuación se muestra el icono de la aplicación y debes proporcionar cadenas de progreso de la interfaz gráfica de usuario y TTS para indicar que la tarea se está controlando activamente.

Cortana muestra una pantalla de progreso durante un máximo de 5 segundos. Transcurridos 5 segundos, Cortana muestra al usuario un mensaje de error y el servicio de la aplicación se cierra. Si el servicio de la aplicación necesita más de 5 segundos para completar la acción, puede seguir actualizando Cortana con pantallas de progreso.

Este es un ejemplo de una pantalla de progreso de la aplicación Adventure Works. En este ejemplo, un usuario ha cancelado un viaje a Las Vegas La pantalla de progreso incluye un mensaje personalizado para la acción, un icono y un icono de contenido con información sobre el viaje cancelado.

Captura de pantalla de Cortana con la pantalla de progreso de la aplicación en segundo plano

AdventureWorksVoiceCommandService.cs contiene el siguiente método de mensaje de progreso, denominado ReportProgressAsync para mostrar la pantalla de progreso en 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);
}

Pantalla de confirmación

Cuando una acción especificada por un comando de voz es irreversible, tiene un impacto significativo o la confianza del reconocimiento no es alta, un servicio de la aplicación puede solicitar confirmación.

Este es un ejemplo de una pantalla de confirmación de la aplicación Adventure Works. En este ejemplo, un usuario indicó al servicio de la aplicación que cancelara un viaje a Las Vegas a través de Cortana. El servicio de la aplicación ha proporcionado a Cortana una pantalla de confirmación que pide al usuario una respuesta de tipo sí o no antes de cancelar el viaje.

Si el usuario dice algo distinto a "Sí" o "No", Cortana no puede determinar la respuesta a la pregunta. En este caso, Cortana solicita confirmación al usuario con una pregunta similar, que proporciona el servicio de la aplicación.

En la segunda solicitud, si el usuario sigue sin decir "Sí" o "No", Cortana solicita confirmación al usuario una tercera vez con la misma pregunta, precedida de una disculpa. Si el usuario sigue sin decir "Sí" o "No", Cortana detiene la escucha de la entrada de voz y pide al usuario que, en su lugar, presiona uno de los botones.

La pantalla de confirmación incluye un mensaje personalizado para la acción, un icono y un icono de contenido con información sobre el viaje cancelado.

Captura de pantalla de Cortana con la pantalla de confirmación de la aplicación en segundo plano

AdventureWorksVoiceCommandService.cs contiene el siguiente método de cancelación de viaje, denominado RequestConfirmationAsync para mostrar una pantalla de confirmación en 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);
			}
		}
	}
}

Pantalla de desambiguación

Cuando una acción especificada por un comando de voz tiene más de un posible resultado, el servicio de una aplicación puede solicitar más información al usuario.

Este es un ejemplo de una pantalla de desambiguación de la aplicación Adventure Works. En este ejemplo, un usuario indicó al servicio de la aplicación que cancelara un viaje a Las Vegas a través de Cortana. Sin embargo, el usuario tiene dos viajes a Las Vegas en fechas diferentes y el servicio de la aplicación no puede completar la acción sin que el usuario seleccione el viaje que desea.

El servicio de la aplicación proporciona a Cortana una pantalla de desambiguación que pide al usuario que realice una selección de una lista de viajes que coinciden, antes de cancelar ninguno.

En este caso, Cortana solicita confirmación al usuario con una pregunta similar, que proporciona el servicio de la aplicación.

En la segunda solicitud, si el usuario aún dice algo que pueda usarse para identificar la selección, Cortana pide confirmación al usuario una tercera vez con la misma pregunta, precedida de una disculpa. Si el usuario sigue sin decir algo que pueda usarse para identificar la selección, Cortana detiene la escucha de la entrada de voz y pide al usuario que, en su lugar, presiona uno de los botones.

La pantalla de desambiguación incluye un mensaje personalizado para la acción, un icono y un icono de contenido con información sobre el viaje cancelado.

Captura de pantalla de Cortana con pantalla de desambiguación de aplicaciones en segundo plano

AdventureWorksVoiceCommandService.cs contiene el siguiente método de cancelación de viaje, denominado RequestDisambiguationAsync para mostrar una pantalla de confirmación en 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;
}

Pantalla de error

Cuando no se puede completar una acción especificada por un comando de voz, el servicio de una aplicación puede proporcionar una pantalla de error.

Este es un ejemplo de una pantalla de error de la aplicación Adventure Works. En este ejemplo, un usuario indicó al servicio de la aplicación que cancelara un viaje a Las Vegas a través de Cortana. Sin embargo, el usuario no tiene ningún viaje programado a Las Vegas.

El servicio de la aplicación proporciona a Cortana una pantalla de error que incluye un mensaje personalizado para la acción, un icono y el mensaje de error concreto.

Llama a ReportFailureAsync para mostrar la pantalla de error en 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);