Mostrar atividades em andamento

Os dispositivos Wear OS são usados com frequência para experiências de longa duração, como o monitoramento de um treino. Essas experiências também podem fornecer informações importantes que o usuário considera relevantes e precisa de acesso rápido.

Isso apresenta um desafio de experiência do usuário: se um usuário iniciar uma tarefa e navegar até o mostrador do relógio, ele ainda poderá fazer o seguinte:

  • Continuar recebendo atualizações importantes.
  • Navegar de volta à tarefa com um toque.

Voltar ao app usando a tela de início pode ser difícil, especialmente quando estiver em movimento, criando um atrito desnecessário.

No Wear OS 7, a solução é parear uma notificação em andamento com um OngoingActivity ou uma notificação de atualização em tempo real. Isso permite que o dispositivo mostre informações sobre a atividade de longa duração na interface do usuário, ativando recursos como o ícone de toque na parte de baixo do mostrador do relógio. Esse indicador mantém os usuários cientes da tarefa em segundo plano e oferece uma maneira de voltar ao app com um toque.

Uma atividade em andamento ou uma atualização em tempo real também mantém o app visível por mais tempo, impedindo que o sistema volte ao mostrador do relógio após um período de inatividade. Para mais informações, consulte Manter seu app visível no Wear.

Use uma atividade em andamento ou uma notificação de atualização em tempo real para experiências que provavelmente durarão mais de um minuto ou em casos em que o usuário provavelmente vai sair do app antes que a tarefa seja concluída.

Usos adequados:treinos monitorados, timers, monitoramento ativo de carona, gravação de voz mais longa, como uma reunião, e listas de compras que precisam estar disponíveis enquanto o usuário está em uma loja.

Usos inadequados:gravação de mensagens de voz curtas, promoções, status de um app em atualização e próximos eventos da agenda.

Importante: o uso de uma OngoingActivity ou atualização em tempo real nesses cenários é um requisito das diretrizes de qualidade do app para Wear OS WO-V4.

Por exemplo, neste app de treino, as informações podem aparecer no mostrador do relógio do usuário como um ícone de corrida que pode ser tocado:

ícone de corrida

Figura 1. Indicador de atividade

Uma notificação em andamento também mostra informações na seção Recentes da tela de início global. Isso oferece outro lugar conveniente para os usuários conferirem o status da tarefa e interagirem novamente com o app:

tela de início

Figura 2. Acesso rápido global

Confira abaixo boas situações para usar uma notificação em andamento vinculada a uma atividade em andamento:

timer

Figura 3. Cronômetro:faz a contagem ativa de tempo decorrido e termina quando o cronômetro é pausado/interrompido.

mapa

Figura 4. Navegação guiada:mostra rotas para um destino. Termina quando o usuário chega ao destino ou interrompe a navegação.

música

Figura 5. Mídia:reproduz música durante uma sessão. Termina imediatamente após o usuário pausar a sessão.

O Wear cria atividades em andamento automaticamente para apps de mídia.

Consulte o codelab sobre Atividades em andamento para um exemplo detalhado da criação dessas atividades para outros tipos de apps.

Configuração

Para começar a usar a API Ongoing Activity no app, adicione as dependências abaixo ao arquivo build.gradle do app:

dependencies {
  implementation "androidx.wear:wear-ongoing:1.1.0"
  implementation "androidx.core:core:1.19.0"
}

Criar uma atividade em andamento

O processo envolve três etapas:

  1. Crie um NotificationCompat.Builder padrão e configure-o como em andamento.
  2. Crie e configure um OngoingActivity objeto, transmitindo o builder de notificação para ele.
  3. Aplique a atividade em andamento ao builder de notificação e poste a notificação resultante.

Criar e configurar a notificação

Comece criando um NotificationCompat.Builder. A etapa principal é chamar setOngoing(true) para marcá-la como uma notificação em andamento. Também é possível definir outras propriedades de notificação nessa fase, como o ícone pequeno e a categoria.

// Create a PendingIntent to pass to the notification builder
val pendingIntent =
    PendingIntent.getActivity(
        this,
        0,
        Intent(this, AlwaysOnActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        },
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
    )

val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("Always On Service")
    .setContentText("Service is running in background")
    .setSmallIcon(R.drawable.animated_walk)
    // Category helps the system prioritize the ongoing activity
    .setCategory(NotificationCompat.CATEGORY_WORKOUT)
    .setContentIntent(pendingIntent)
    .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
    .setOngoing(true) // Important!

Criar a OngoingActivity

Em seguida, crie uma instância de OngoingActivity usando o builder. O OngoingActivity.Builder exige um Context, um ID de notificação e o NotificationCompat.Builder criado na etapa anterior.

Configure as principais propriedades que serão mostradas nas novas plataformas da interface:

  • Ícones animados e estáticos: forneça ícones que são mostrados no mostrador do relógio nos modos ativo e ambiente.
  • Intent de toque: uma PendingIntent que leva o usuário de volta ao app quando ele toca no ícone de atividade em andamento. É possível reutilizar o pendingIndent criado na etapa anterior.

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // Sets the icon that appears on the watch face in active mode.
        .setAnimatedIcon(R.drawable.animated_walk)
        // Sets the icon that appears on the watch face in ambient mode.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap target to bring the user back to the app.
        .setTouchIntent(pendingIntent)
        .build()

Aplicar à notificação e postar

A etapa final é vincular a OngoingActivity à notificação e postá-la. O método ongoingActivity.apply() modifica o builder de notificação original, adicionando os dados necessários para que o sistema possa mostrá-lo nas plataformas extras. Depois de aplicar, é possível criar e postar a notificação como de costume.

// This call modifies notificationBuilder to include the ongoing activity data.
ongoingActivity.apply(applicationContext)

// Post the notification.
startForeground(NOTIFICATION_ID, notificationBuilder.build())

Adicionar texto de status dinâmico à tela de início

O código anterior adiciona o ícone de toque ao mostrador do relógio. Para fornecer atualizações ainda mais ricas e em tempo real na seção Recentes da tela de início, crie um Status objeto e anexe-o à OngoingActivity. Se você não fornecer um Status, o sistema vai usar o texto de conteúdo da notificação (definido usando setContentText()). Para mostrar texto dinâmico, use um Status.Builder. É possível definir uma string de modelo com marcadores de posição e fornecer objetos Status.Part para preencher esses marcadores. O Status.Part pode ser dinâmico, como um cronômetro ou timer.

O exemplo a seguir mostra como criar um status que exibe "Executar por [um timer de cronômetro]":

// Define a template with placeholders for the activity type and the timer.
val statusTemplate = "#type# for #time#"

// Set the start time for a stopwatch.
// Use SystemClock.elapsedRealtime() for time-based parts.
val runStartTime = SystemClock.elapsedRealtime()

val ongoingActivityStatus = Status.Builder()
    // Sets the template string.
    .addTemplate(statusTemplate)
    // Fills the #type# placeholder with a static text part.
    .addPart("type", Status.TextPart("Run"))
    // Fills the #time# placeholder with a stopwatch part.
    .addPart("time", Status.StopwatchPart(runStartTime))
    .build()

Por fim, vincule este Status ao seu OngoingActivity chamando setStatus() no OngoingActivity.Builder.

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // Add the status to the OngoingActivity.
        .setStatus(ongoingActivityStatus)
        .build()

Outras personalizações

Além de Status, é possível personalizar sua atividade em andamento ou notificações das seguintes maneiras. No entanto, essas personalizações podem não ser usadas, com base na implementação do OEM.

Notificação em andamento

  • A categoria definida determina a prioridade da atividade em andamento.
    • CATEGORY_CALL:uma ligação ou videochamada recebida ou uma solicitação de comunicação síncrona semelhante
    • CATEGORY_NAVIGATION:um mapa ou uma navegação guiada
    • CATEGORY_TRANSPORT: controle de transporte de mídia para reprodução
    • CATEGORY_ALARM:um alarme ou timer
    • CATEGORY_WORKOUT:um treino
    • CATEGORY_LOCATION_SHARING: categoria de compartilhamento temporário de local
    • CATEGORY_STOPWATCH:cronômetro

Atividade em andamento

  • Ícone animado: um vetor em preto e branco, de preferência com um segundo plano transparente. Aparece no mostrador do relógio no modo ativo. Se o ícone animado não for fornecido, o ícone de notificação padrão vai ser usado. O ícone de notificação padrão é diferente para cada aplicativo.

  • Ícone estático:ícone vetorial com segundo plano transparente. Aparece no mostrador do relógio no modo ambiente. Se o ícone animado não estiver definido, o ícone estático vai ser usado no mostrador do relógio no modo ativo. Se esse valor não for fornecido, o ícone de notificação vai ser usado. Se nenhum deles for definido, uma exceção será lançada. A tela de início ainda vai usar o ícone do app.

  • OngoingActivityStatus:texto simples ou um Chronometer. Aparece na seção Recentes da tela de início. Se não for informado, vai ser usada a notificação "texto de contexto".

  • Intent de toque:uma PendingIntent usada para voltar ao app se o usuário tocar no ícone de atividade em andamento. Aparece no mostrador do relógio ou no item da tela de início. Pode ser diferente da intent original usada para iniciar o app. Se não for fornecida, a intent de conteúdo da notificação será usada. Se nenhuma delas for definida, uma exceção será lançada.

  • LocusId: um ID que atribui o atalho da tela de início a que a atividade em andamento corresponde. Aparece na tela de início na seção Recentes enquanto a atividade está em andamento. Se não for fornecido, a tela de início vai ocultar todos os itens do app na seção Recentes do mesmo pacote e mostrar apenas a atividade em andamento.

  • ID de atividade em andamento: um ID usado para diferenciar as chamadas para fromExistingOngoingActivity() quando um aplicativo tem mais de uma atividade em andamento.

Atualizar uma atividade em andamento

Atualize a atividade em andamento para a notificação atual quando precisar mudar o status, em vez de criar uma nova notificação e atividade em andamento. Para atualizar a atividade em andamento e a notificação postada, use o objeto criado antes e chame update(), conforme mostrado no exemplo a seguir:

ongoingActivity.update(context, newStatus)

Nos casos em que não é possível armazenar uma referência à atividade em andamento, há um método estático para recuperá-la. No entanto, essa opção é menos recomendada:

OngoingActivity.recoverOngoingActivity(context)
    ?.update(context, newStatus)

Interromper uma atividade em andamento

Quando o app termina de ser executado como uma atividade em andamento, ele só precisa cancelar a notificação em andamento.

Também é possível cancelar a notificação ou a atividade em andamento quando ela está em primeiro plano e, depois, recriá-la quando voltar ao segundo plano, mas isso não é necessário.

Pausar uma atividade em andamento

Se o app tiver uma ação de interrupção explícita, continue a atividade em andamento depois que ela for retomada. Para um app sem uma ação de parada explícita, encerre a atividade quando ela estiver pausada.

Principais considerações

Não esqueça dos princípios abaixo ao trabalhar com a API Ongoing Activity:

Publicar notificações ao abrir mídia em dispositivos Wear OS

Se estiver tocando conteúdo de mídia em um dispositivo Wear OS, publique uma notificação de mídia. Isso permite que o sistema crie a atividade em andamento correspondente.

Se você estiver usando a Media3, a notificação será publicada automaticamente. Se você criar sua notificação manualmente, ela vai usar o MediaStyleNotificationHelper.MediaStyle, e a MediaSession correspondente terá a atividade da sessão preenchida.