Compartilhar via


Link profundo de um aplicativo em segundo plano na Cortana para um aplicativo em primeiro plano

Aviso

Não há mais suporte para este recurso a partir da atualização de maio de 2020 do Windows 10 (versão 2004, codinome "20H1").

Forneça links profundos de um aplicativo em segundo plano na Cortana que iniciem o aplicativo em primeiro plano em um estado ou contexto específico.

Observação

A Cortana e o serviço de aplicativo em segundo plano são encerrados quando o aplicativo em primeiro plano é iniciado.

Um link profundo é exibido por padrão na tela de conclusão da Cortana, conforme mostrado aqui ("Ir para o AdventureWorks"). Porém, você pode exibir links profundos em várias outras telas.

Captura de tela da conclusão do aplicativo em segundo plano da Cortana para uma próxima viagem

Visão geral

Os usuários podem acessar o aplicativo por meio da Cortana, fazendo o seguinte:

Discutimos a vinculação profunda aqui.

A vinculação profunda é útil quando a Cortana e seu serviço de aplicativo atuam como gateway para seu aplicativo completo (em vez de exigir que o usuário inicie o aplicativo por meio do menu Iniciar) ou para fornecer acesso a detalhes e funcionalidades mais ricos no seu aplicativo que não são possíveis com a Cortana. A vinculação profunda é outra maneira de aumentar a usabilidade e promover o aplicativo.

Há três maneiras de fornecer links profundos:

  • Um link "Ir para o <aplicativo>" em várias telas da Cortana.
  • Um link incorporado em um bloco de conteúdo em várias telas da Cortana .
  • Iniciando programaticamente o aplicativo em primeiro plano pelo serviço de aplicativo em segundo plano.

A Cortana exibe um link profundo "Ir para o <aplicativo>" abaixo do cartão de conteúdo na maioria das telas.

Captura de tela do link direto

Você pode fornecer um argumento de inicialização para esse link que abre seu aplicativo em um contexto semelhante ao serviço de aplicativo. Se você não fornecer um argumento de inicialização, o aplicativo será iniciado na tela principal.

Neste exemplo de AdventureWorksVoiceCommandService.cs da amostra AdventureWorks, passamos a cadeia de caracteres de destino (destination) especificada para o método SendCompletionMessageForDestination, que recupera todas as viagens correspondentes e fornece um link profundo para o aplicativo.

Primeiro, criamos uma VoiceCommandUserMessage (userMessage) que é falado pela Cortana e mostrado na tela da Cortana. Um objeto de lista VoiceCommandContentTile é criado para exibir a coleção de cartões de resultados na tela.

Esses dois objetos são então transmitidos ao método CreateResponse do objeto VoiceCommandResponse (response). Em seguida, definimos o valor da propriedade AppLaunchArgument do objeto de resposta como o valor de destination transmitido a essa função. Quando um usuário toca em um bloco de conteúdo na tela da Cortana, os valores de parâmetro são transmitidos ao aplicativo por meio do objeto de resposta.

Finalmente, chamamos o método ReportSuccessAsync de 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);
}

É possível adicionar links profundos a cartões de conteúdo em várias telas da Cortana.

Captura de tela da tela da Cortana para fluxo de aplicativo em segundo plano da Cortana de ponta a ponta usando a próxima viagem do AdventureWorks com entrega"Próxima viagem" da AdventureWorks com tela de transferência

Como os links "Ir para o <aplicativo>", você pode fornecer um argumento de inicialização para abrir seu aplicativo com contexto semelhante ao serviço de aplicativo. Se um argumento de inicialização não for fornecido, o bloco de conteúdo não será vinculado ao seu aplicativo.

Neste exemplo de AdventureWorksVoiceCommandService.cs da amostra AdventureWorks, transmitidos o destino especificado ao método SendCompletionMessageForDestination, que recupera todas as viagens correspondentes e fornece cartões de conteúdo com links profundos para o aplicativo.

Primeiro, criamos uma VoiceCommandUserMessage (userMessage) que é falado pela Cortana e mostrado na tela da Cortana. Um objeto de lista VoiceCommandContentTile é criado para exibir a coleção de cartões de resultados na tela.

Esses dois objetos são então transmitidos ao método CreateResponse do objeto VoiceCommandResponse (response). Em seguida, definimos o valor da propriedade AppLaunchArgument como o valor do destino no comando de voz.

Finalmente, chamamos o método ReportSuccessAsync de VoiceCommandServiceConnection. Adicionamos dois blocos de conteúdo com valores de parâmetro AppLaunchArgument diferentes a uma lista VoiceCommandContentTile usada na chamada ReportSuccessAsync do objeto 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);
}

Você também pode iniciar o aplicativo programaticamente com um argumento de inicialização para abri-lo com contexto semelhante ao serviço de aplicativo. Se você não fornecer um argumento de inicialização, o aplicativo será iniciado na tela principal.

Adicionamos um parâmetro AppLaunchArgument com um valor de "Las Vegas" a um objeto VoiceCommandResponse usado na chamada RequestAppLaunchAsync do objeto 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 de aplicativo

Para habilitar a vinculação profunda ao aplicativo, você deve declarar a extensão windows.personalAssistantLaunch no arquivo Package.appxmanifest do seu projeto de aplicativo.

Aqui, declaramos a extensão windows.personalAssistantLaunch para o aplicativo Adventure Works.

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

Contrato de protocolo

O aplicativo é iniciado em primeiro plano por meio da ativação do URI usando um contrato de Protocolo. O aplicativo deve substituir o evento OnActivated do aplicativo e verificar se há um ActivationKind de Protocolo. Para obter mais informações, consulte Lidar com a ativação de URI.

Aqui, decodificamos o URI fornecido pelo ProtocolActivatedEventArgs para acessar o argumento de inicialização. Para este exemplo, o Uri é definido como "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();
  }