Codelab da API Activity Recognition Transition

1. Introdução

Carregamos smartphones em todos os lugares, mas até agora era difícil para os apps ajustar a experiência ao ambiente e à atividade em constante mudança do usuário.

Para fazer isso no passado, os desenvolvedores gastavam um tempo valioso de engenharia combinando vários indicadores (localização, sensor etc.) para determinar quando uma atividade como caminhar ou dirigir começou ou terminou. Pior ainda, quando os apps verificam de forma independente e contínua as mudanças na atividade do usuário, a duração da bateria é afetada.

A API Activity Recognition Transition resolve esses problemas fornecendo uma API simples que faz todo o processamento para você e informa apenas o que é importante para você: quando a atividade de um usuário muda. Seu app simplesmente se inscreve em uma transição nas atividades em que você tem interesse, e a API notifica você sobre as mudanças.

Por exemplo, um app de mensagens pode perguntar "avise quando o usuário entrar ou sair de um veículo" para definir o status do usuário como ocupado. Da mesma forma, um app de detecção de estacionamento pode perguntar "avise-me quando o usuário sair de um veículo e começar a caminhar" para salvar o local de estacionamento do usuário.

Neste codelab, você vai aprender a usar a API Activity Recognition Transition para determinar quando um usuário inicia/interrompe uma atividade, como caminhar ou correr.

Pré-requisitos

Ter familiaridade com o desenvolvimento para Android e com callbacks.

O que você aprenderá

  • Fazer o registro para transições de atividades
  • Processar esses eventos
  • Cancelar o registro de transições de atividades quando elas não forem mais necessárias

O que é necessário

  • Android Studio Bumblebee
  • Um dispositivo ou emulador Android

2. Como começar

Clonar o repositório inicial do projeto

Para começar o mais rápido possível, preparamos um projeto inicial para você desenvolver. Caso você tenha o git instalado, basta executar o comando abaixo. Para verificar se o git está instalado, digite git --version na linha de comando ou terminal e veja se ele é executado corretamente.

 git clone https://github.com/android/codelab-activity_transitionapi

Se você não tiver o git, faça o download do projeto como um arquivo ZIP:

Importar o projeto

Inicie o Android Studio, selecione "Open an existing Android Studio project" na tela inicial e abra o diretório do projeto.

Após o carregamento do projeto, você também verá um alerta informando que o Git não está rastreando todas as mudanças locais. Clique em Ignore ou no X no canto superior direito. Você não enviará mudanças ao repositório Git.

No canto superior esquerdo da janela do projeto, você vai encontrar algo parecido com a imagem abaixo se estiver na visualização Android. Se você estiver na visualização Project, precisará expandi-la para ver a mesma coisa.

d2363db913d8e5ad.png

Há dois ícones de pasta (base e complete). Cada um deles é conhecido como "módulo".

O Android Studio pode levar vários segundos para compilar o projeto em segundo plano pela primeira vez. Durante esse período, você verá um ícone de carregamento na barra de status, que fica na parte de baixo do Android Studio:

c9f23d5336be3cfe.png

Recomendamos aguardar até que isso termine antes de fazer mudanças no código. Isso permitirá que o Android Studio extraia todos os componentes necessários.

Além disso, se você receber uma mensagem que diz "Reload for language changes to take effect?" ou algo semelhante, selecione "Yes".

Entender o projeto inicial

Está tudo pronto para você adicionar o reconhecimento de atividade. Usaremos o módulo base, que é o ponto de partida para este codelab. Em outras palavras, você adicionará um código de cada etapa a base.

O módulo complete pode ser usado para verificar seu trabalho ou para consulta em caso de problemas.

Visão geral dos componentes principais:

  • MainActivity: contém todo o código necessário para o reconhecimento de atividades.

Configuração do emulador

Se precisar de ajuda para configurar um emulador do Android, consulte o artigo Executar seu app.

Executar o projeto inicial

Vamos executar o app.

  • Conecte o dispositivo Android ao computador ou inicie um emulador.
  • Na barra de ferramentas, selecione a configuração base no seletor suspenso e clique no botão de triângulo verde (Executar) ao lado dele:

a640a291ffaf62ad.png

  • Você vai encontrar o aplicativo abaixo:

f58d4bb92ee77f41.png

  • O app não faz nada além de mostrar uma mensagem. Agora vamos adicionar o reconhecimento de atividade.

Resumo

Nesta etapa, você aprendeu sobre:

  • Configuração geral do codelab.
  • Noções básicas do app.
  • Como implantar o app.

3. Analisar a biblioteca e adicionar permissão ao manifesto

Para usar a API Transition no app, você precisa declarar uma dependência à API Google Location and Activity Recognition e especificar a permissão com.google.android.gms.permission.ACTIVITY_RECOGNITION no manifesto do app.

  1. Pesquise TODO: Review play services library required for activity recognition no arquivo build.gradle. Não há ações nessa etapa (etapa 1). Apenas analise a dependência declarada de que precisamos. Ele vai ter esta aparência:
    // TODO: Review play services library required for activity recognition.
    implementation 'com.google.android.gms:play-services-location:19.0.1'
  1. No módulo base, pesquise TODO: Add both activity recognition permissions to the manifest no AndroidManifest.xml e adicione o código abaixo ao elemento <manifest>.
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

Agora, seu código ficará assim:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.example.myapp">
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

  ...
</manifest>

Como é possível ver nos comentários, é necessário adicionar uma segunda permissão para o Android 10. Isso é necessário para a permissão de execução que foi adicionada na versão 29 da API.

Pronto! Agora seu app é compatível com o reconhecimento de atividade. Basta adicionar o código para receber as informações.

Executar o app

Execute o app no Android Studio. Ele deve ser exatamente igual. Ainda não adicionamos nenhum código para monitorar as transições. Isso será feito na próxima seção.

4. Como verificar/solicitar permissões de execução no Android

Embora tenhamos permissão para a versão 28 e anteriores da API, precisamos oferecer suporte a permissões de execução na versão 29 e mais recentes:

  • No MainActivity.java, vamos verificar se o usuário está no Android 10 (29) ou mais recente e, se estiver, vamos verificar as permissões de reconhecimento de atividade.
  • Se as permissões não forem concedidas, vamos enviar o usuário para uma tela de apresentação (PermissionRationalActivity.java) explicando por que o app precisa da permissão e permitir que ele a aprove.

Revise o código que verifica a versão do Android

No módulo base, pesquise TODO: Review check for devices with Android 10 (29+) em MainActivity.java. Você verá este snippet de código.

Não há ações para esta seção.

// TODO: Review check for devices with Android 10 (29+).
private boolean runningQOrLater =
    android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q;

Como mencionado anteriormente, você precisa de aprovação para a permissão de execução android.permission.ACTIVITY_RECOGNITION no Android 10 e versões mais recentes. Usamos essa verificação simples para decidir se precisamos ou não verificar as permissões de execução.

Revisar a verificação de permissão de execução para o reconhecimento de atividade, se necessário

No módulo base, pesquise TODO: Review permission check for 29+ em MainActivity.java. Você verá este snippet de código.

Não há ações para esta seção.

// TODO: Review permission check for 29+.
if (runningQOrLater) {

   return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
           this,
           Manifest.permission.ACTIVITY_RECOGNITION
   );
} else {
   return true;
}

Usamos a variável criada na etapa anterior para saber se precisamos verificar as permissões de execução.

Para Q e versões mais recentes, verificamos e retornamos o resultado da permissão de execução. Isso faz parte de um método maior chamado activityRecognitionPermissionApproved(), que informa ao desenvolvedor, em uma chamada simples, se precisamos solicitar uma permissão ou não.

Solicitar permissões de execução e ativar/desativar transições de reconhecimento de atividade

No módulo base, pesquise TODO: Enable/Disable activity tracking and ask for permissions if needed em MainActivity.java. Adicione o código abaixo após o comentário.

// TODO: Enable/Disable activity tracking and ask for permissions if needed.
if (activityRecognitionPermissionApproved()) {

   if (activityTrackingEnabled) {
      disableActivityTransitions();

   } else {
      enableActivityTransitions();
   }

} else {  
   // Request permission and start activity for result. If the permission is approved, we
   // want to make sure we start activity recognition tracking.
   Intent startIntent = new Intent(this, PermissionRationalActivity.class);
   startActivityForResult(startIntent, 0);

}

Aqui, perguntamos se o reconhecimento de atividade foi aprovado. Se ele estiver e o reconhecimento de atividade já estiver ativado, vamos desativá-lo. Caso contrário, vamos ativar.

Quando a permissão não é aprovada, enviamos o usuário para a atividade na tela de apresentação, que explica por que precisamos da permissão, e permitimos que ele a ative.

Revise o código de solicitação de permissão

No módulo base, pesquise TODO: Review permission request for activity recognition em PermissionRationalActivity.java. Você verá este snippet de código.

Não há ações para esta seção.

// TODO: Review permission request for activity recognition.
ActivityCompat.requestPermissions(
             this,
             new String[]{Manifest.permission.ACTIVITY_RECOGNITION},
             PERMISSION_REQUEST_ACTIVITY_RECOGNITION)

Essa é a parte mais importante da atividade e a que você precisa revisar. O código aciona a solicitação de permissão quando o usuário a solicita.

Além disso, a classe PermissionRationalActivity.java mostra um motivo para o usuário aprovar a permissão de reconhecimento de atividade (prática recomendada). O usuário pode clicar no botão No Thanks ou Continue (que aciona o código acima).

Confira o arquivo para saber mais.

5. Registrar/cancelar o registro do receiver para transições de atividades

Antes de configurar o código de reconhecimento de atividade, queremos garantir que a atividade possa processar ações de transição geradas pelo sistema.

Criar um BroadcastReceiver para a transição

No módulo base, pesquise TODO: Create a BroadcastReceiver to listen for activity transitions em MainActivity.java. Cole o snippet abaixo.

// TODO: Create a BroadcastReceiver to listen for activity transitions.
// The receiver listens for the PendingIntent above that is triggered by the system when an
// activity transition occurs.
mTransitionsReceiver = new TransitionsReceiver();

Registrar um BroadcastReceiver para a transição

No módulo base, pesquise TODO: Register a BroadcastReceiver to listen for activity transitions em MainActivity.java. Ele está em onStart(). Cole o snippet abaixo.

// TODO: Register a BroadcastReceiver to listen for activity transitions.
registerReceiver(mTransitionsReceiver, new IntentFilter(TRANSITIONS_RECEIVER_ACTION));

Agora temos uma maneira de receber atualizações quando as transições de atividade são geradas pelo PendingIntent.

Desregistrar o BroadcastReceiver

No módulo base, pesquise Unregister activity transition receiver when user leaves the app em MainActivity.java. Ele está em onStop(). Cole o snippet abaixo.

// TODO: Unregister activity transition receiver when user leaves the app.
unregisterReceiver(mTransitionsReceiver);

É uma prática recomendada cancelar o registro de um receiver quando o Activity está sendo encerrado.

6. Configurar transições de atividades e solicitar atualizações

Para começar a receber atualizações de transição de atividade, implemente:

Criar uma lista de transições de atividade a seguir

Para criar o objeto ActivityTransitionRequest, crie uma lista de objetos ActivityTransition, que representam a transição que você quer acompanhar. Um objeto ActivityTransition inclui os seguintes dados:

  1. Um tipo de atividade, representado pela classe DetectedActivity. A API Transition é compatível com as seguintes atividades:
  1. Um tipo de transição, representado pela classe ActivityTransition. Os tipos de transição são:

No módulo base, pesquise TODO: Add activity transitions to track em MainActivity.java. Adicione o código abaixo após o comentário.

// TODO: Add activity transitions to track.
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.WALKING)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.WALKING)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.STILL)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.STILL)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
        .build());

Esse código adiciona as transições que queremos rastrear a uma lista que estava vazia.

Criar uma PendingIntent

Como mencionado anteriormente, precisamos de um PendingIntent se quisermos receber alertas sobre qualquer mudança na ActivityTransitionRequest. Portanto, antes de configurar a ActivityTransitionRequest, precisamos criar um PendingIntent.

No módulo base, pesquise TODO: Initialize PendingIntent that will be triggered when a activity transition occurs em MainActivity.java. Adicione o código abaixo após o comentário.

// TODO: Initialize PendingIntent that will be triggered when a activity transition occurs.
Intent intent = new Intent(TRANSITIONS_RECEIVER_ACTION);
mActivityTransitionsPendingIntent =
        PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0);

Agora temos uma PendingIntent que pode ser acionada quando uma das transições de atividade ocorre.

Criar uma ActivityTransitionRequest e solicitar atualizações

Você pode criar um objeto ActivityTransitionRequest transmitindo a lista de ActivityTransitions para a classe ActivityTransitionRequest.

No módulo base, pesquise Create request and listen for activity changes em MainActivity.java. Adicione o código abaixo após o comentário.

// TODO: Create request and listen for activity changes.
ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);

// Register for Transitions Updates.
Task<Void> task =
        ActivityRecognition.getClient(this)
                .requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);


task.addOnSuccessListener(
        new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
                activityTrackingEnabled = true;
                printToScreen("Transitions Api was successfully registered.");

            }
        });
task.addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions Api could NOT be registered: " + e);
                Log.e(TAG, "Transitions Api could NOT be registered: " + e);

            }
        });

Vamos analisar o código. Primeiro, criamos uma ActivityTransitionRequest na lista de transições de atividade.

ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);

Em seguida, nos inscrevemos para atualizações de transição de atividades transmitindo sua instância de ActivityTransitionRequest e nosso objeto PendingIntent criado na última etapa para o método requestActivityTransitionUpdates(). O método requestActivityTransitionUpdates() retorna um objeto Task que pode ser verificado quanto a sucesso ou falha, conforme mostrado no próximo bloco do código:

// Register for Transitions Updates.
Task<Void> task =
        ActivityRecognition.getClient(this)
                .requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);


task.addOnSuccessListener(
        new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
                activityTrackingEnabled = true;
                printToScreen("Transitions Api was successfully registered.");

            }
        });
task.addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions Api could NOT be registered: " + e);
                Log.e(TAG, "Transitions Api could NOT be registered: " + e);

            }
        });

Depois de se inscrever para receber atualizações de transição de atividades, seu app recebe notificações na PendingIntent registrada. Também definimos uma variável para que o rastreamento de atividades seja ativado, para saber se é necessário desativar/ativar se o usuário clicar no botão novamente.

Remover atualizações ao fechar o app

É importante remover as atualizações de transição quando o app for fechado.

No módulo base, pesquise Stop listening for activity changes em MainActivity.java. Adicione o código abaixo após o comentário.

// TODO: Stop listening for activity changes.
ActivityRecognition.getClient(this).removeActivityTransitionUpdates(mActivityTransitionsPendingIntent)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                activityTrackingEnabled = false;
                printToScreen("Transitions successfully unregistered.");
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions could not be unregistered: " + e);
                Log.e(TAG,"Transitions could not be unregistered: " + e);
            }
        });

Agora precisamos chamar o método que contém o código acima quando o app for encerrado.

No módulo base, pesquise TODO: Disable activity transitions when user leaves the app em MainActivity.java em onPause(). Adicione o código abaixo após o comentário.

// TODO: Disable activity transitions when user leaves the app.
if (activityTrackingEnabled) {
    disableActivityTransitions();
}

É isso. Você já sabe como acompanhar as mudanças nas transições de atividades. Agora só precisamos processar as atualizações.

7. Como processar eventos

Quando a transição da atividade solicitada ocorre, seu app recebe um callback de intent. Um objeto ActivityTransitionResult pode ser extraído da intent, que inclui uma lista de objetos ActivityTransitionEvent. Os eventos são organizados em ordem cronológica. Por exemplo, se um app solicita o tipo de atividade IN_VEHICLE nas transições ACTIVITY_TRANSITION_ENTER e ACTIVITY_TRANSITION_EXIT, ele recebe um objeto ActivityTransitionEvent quando o usuário começa a dirigir e outro quando o usuário faz a transição para qualquer outra atividade.

Vamos adicionar o código para lidar com esses eventos.

No módulo base, pesquise TODO: Extract activity transition information from listener em MainActivity.java em onReceive() do BroadcastReceiver que criamos anteriormente. Adicione o código abaixo após o comentário.

// TODO: Extract activity transition information from listener.
if (ActivityTransitionResult.hasResult(intent)) {

    ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);

    for (ActivityTransitionEvent event : result.getTransitionEvents()) {

        String info = "Transition: " + toActivityString(event.getActivityType()) +
                " (" + toTransitionType(event.getTransitionType()) + ")" + "   " +
                new SimpleDateFormat("HH:mm:ss", Locale.US).format(new Date());

        printToScreen(info);
    }
}

Isso vai converter as informações em um String e imprimi-las na tela.

É isso. Está tudo pronto! Tente executar o app.

OBSERVAÇÃO IMPORTANTE: é difícil reproduzir mudanças de atividade no emulador. Por isso, recomendamos usar um dispositivo físico.

Você pode acompanhar as mudanças de atividade.

Para melhores resultados, instale o app em um dispositivo físico e caminhe. :)

8. Revisar o código

Você criou um app simples que rastreia as transições de atividades e as lista na tela.

Você poderá ler o código completo para revisar o que foi feito e entender melhor como ele funciona.