Manter seu app visível no Wear

Alguns apps para Wear OS ficam constantemente visíveis para o usuário.

Os dispositivos Wear OS com Android 5.1 ou posterior permitem que apps permaneçam em primeiro plano e economizem bateria ao mesmo tempo. Os apps Wear OS podem controlar o que é exibido no smartwatch enquanto ele está no modo de baixa energia (ambiente). Apps Wear que são executados no modo ambiente e no modo interativo são chamados de apps sempre ativos.

Esses apps permitem, por exemplo, que usuários olhem o smartwatch e vejam a distância e o tempo percorridos enquanto correm. Alguns usuários registram listas de compras e podem ver rapidamente os itens, caso necessário.

Tornar um app constantemente visível tem um impacto na duração da bateria, portanto, considere com cuidado esse impacto ao adicionar esse recurso ao app.

Importante: a versão 27.1.0 da Biblioteca de Suporte do Android permite uma nova maneira de oferecer compatibilidade com o modo ambiente, que usa a classe AmbientModeSupport em vez da classe WearableActivity. Você pode decidir se quer usar a nova maneira preferencial para oferecer compatibilidade com o modo ambiente ou se quer estender a classe WearableActivity.

Observação: a classe AmbientMode se tornou obsoleta e foi substituída pela classe AmbientModeSupport.

Confira os seguintes recursos relacionados:

Configurar seu projeto

Para oferecer compatibilidade com o modo ambiente, você precisa atualizar o SDK do Android e configurar o projeto de desenvolvimento. Siga estas etapas para fazer as alterações necessárias:

  1. Crie ou atualize seu projeto com base nas configurações da página Criar e executar um app para wearables.
  2. Adicione a permissão WAKE_LOCK ao arquivo de manifesto do Android:
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

Modo ambiente usando a classe AmbientModeSupport

Usar a classe AmbientModeSupport para oferecer compatibilidade com o modo ambiente permite que você se beneficie de:

Para usar a classe AmbientModeSupport, estenda uma das subclasses FragmentActivity ou a própria FragmentActivity e implementar uma interface de provedor, que por sua vez, pode ser usada para ouvir atualizações do modo ambiente.

Observação: o método AmbientModeSupport.attach(FragmentActivity) anexa um fragmento headless à classe ou subclasse FragmentActivity fornecida, chamando subsequentemente FragmentManager.getFragments() para retornar uma referência a esse fragmento, que não precisa ser usado.

As etapas a seguir descrevem o uso geral da classe AmbientModeSupport:

  1. Crie uma subclasse de uma das classes FragmentActivity.
  2. Implemente a interface AmbientCallbackProvider, como no exemplo abaixo. Substitua o método getAmbientCallback() para fornecer os callbacks necessários para reagir a eventos de ambiente do sistema Android. Na Etapa 4, criaremos a classe de callback personalizada.

    Kotlin

        class MainActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbackProvider {
            …
            override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback = MyAmbientCallback()
            …
        }
        

    Java

        public class MainActivity extends AppCompatActivity implements AmbientModeSupport.AmbientCallbackProvider {
            …
            @Override
            public AmbientModeSupport.AmbientCallback getAmbientCallback() {
                return new MyAmbientCallback();
            }
            …
        }
        
  3. No método onCreate(), ative o modo ambiente chamando AmbientModeSupport.attach(FragmentActivity). Esse método retorna um AmbientModeSupport.AmbientController. O controlador permite verificar o estado do ambiente fora dos callbacks. É recomendável manter uma referência ao objeto AmbientModeSupport.AmbientController:

    Kotlin

        class MainActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbackProvider {
            ...
            /*
             * Declare an ambient mode controller, which will be used by
             * the activity to determine if the current mode is ambient.
             */
            private lateinit var ambientController: AmbientModeSupport.AmbientController
            ...
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                ...
                ambientController = AmbientModeSupport.attach(this)
            }
            ...
        }
        

    Java

        public class MainActivity extends AppCompatActivity implements AmbientModeSupport.AmbientCallbackProvider {
            ...
            /*
             * Declare an ambient mode controller, which will be used by
             * the activity to determine if the current mode is ambient.
             */
            private AmbientModeSupport.AmbientController ambientController;
            ...
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                ...
                ambientController = AmbientModeSupport.attach(this);
            }
            ...
        }
        
  4. Crie uma classe interna que estenda a classe AmbientCallback para agir em eventos de ambiente. Essa classe se torna o objeto retornado do método criado na Etapa 2:

    Kotlin

        private class MyAmbientCallback : AmbientModeSupport.AmbientCallback() {
    
            override fun onEnterAmbient(ambientDetails: Bundle?) {
              // Handle entering ambient mode
            }
    
            override fun onExitAmbient() {
              // Handle exiting ambient mode
            }
    
            override fun onUpdateAmbient() {
              // Update the content
            }
        }
        

    Java

        private class MyAmbientCallback extends AmbientModeSupport.AmbientCallback {
            @Override
            public void onEnterAmbient(Bundle ambientDetails) {
              // Handle entering ambient mode
            }
    
            @Override
            public void onExitAmbient() {
              // Handle exiting ambient mode
             }
    
            @Override
            public void onUpdateAmbient() {
              // Update the content
            }
        }
        

Leia as seções da amostra de AlwaysOn para mais informações e práticas recomendadas.

Modo ambiente usando a classe WearableActivity

Para projetos novos e existentes, você pode adicionar compatibilidade com o modo ambiente ao app Wear ao atualizar a configuração do seu projeto.

Criar uma atividade compatível com o modo ambiente

Você pode ativar o modo ambiente na sua atividade usando a classe WearableActivity:

  1. Crie uma atividade que WearableActivity.
  2. No método onCreate() da sua atividade, chame o método setAmbientEnabled().

Ative o modo ambiente na sua atividade da seguinte maneira:

Kotlin

    class MainActivity : WearableActivity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            setAmbientEnabled()
        ...
        }
    }

Java

    public class MainActivity extends WearableActivity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setAmbientEnabled();
            ...
        }
    

Processar transições entre modos

Se o usuário não interagir com seu app por um período enquanto ele estiver sendo exibido ou se o usuário cobrir a tela com a palma da mão, o sistema alternará a atividade para o modo ambiente. Depois que o app for alternado para o modo ambiente, atualize a IU da atividade para um layout mais básico a fim de reduzir o consumo de energia. Use um plano de fundo preto com o mínimo de elementos gráficos e textos brancos. Para facilitar a transição do modo interativo para o ambiente, tente manter um posicionamento de itens semelhante na tela. Para mais informações sobre apresentação de conteúdo no modo ambiente, consulte o guia de design Mostradores de relógio para Wear OS.

Quando seu app é executado em um dispositivo sem um botão físico, colocar a mão sobre a tela não alterna o app para o modo ambiente. Em vez disso, essa ação faz com que o app seja fechado e a tela inicial apareça. Assim, usuários podem sair dos apps facilmente. No entanto, esses dispositivos ainda entram no modo ambiente quando a tela fica ociosa por muito tempo.

Observação: no modo ambiente, desative todos os elementos interativos na tela, como botões. Para mais informações sobre como criar interações do usuário em um app sempre ativo, consulte o guia de design Estrutura do app para o Wear OS (em inglês).

Quando a atividade muda para o modo ambiente, o sistema chama o método onEnterAmbient() na atividade do wearable. O snippet de código a seguir mostra como alterar a cor do texto para branco e desativar o anti-aliasing quando o sistema alternar para o modo ambiente:

Kotlin

    override fun onEnterAmbient(ambientDetails: Bundle?) {
        super.onEnterAmbient(ambientDetails)

        stateTextView.setTextColor(Color.WHITE)
        stateTextView.paint.isAntiAlias = false
    }
    

Java

    @Override
    public void onEnterAmbient(Bundle ambientDetails) {
        super.onEnterAmbient(ambientDetails);

        stateTextView.setTextColor(Color.WHITE);
        stateTextView.getPaint().setAntiAlias(false);
    }
    

Quando o usuário toca na tela ou levanta o pulso, a atividade alterna do modo ambiente para o modo interativo. O sistema chama o método onExitAmbient(). Modifique esse método para atualizar o layout da IU para que seu app seja exibido em um estado interativo colorido.

O snippet de código a seguir mostra como alterar a cor do texto para verde e ativar o anti-aliasing quando o sistema alternar para o modo interativo:

Kotlin

    override fun onExitAmbient() {
        super.onExitAmbient()

        stateTextView.setTextColor(Color.GREEN)
        stateTextView.paint.isAntiAlias = true
    }
    

Java

    @Override
    public void onExitAmbient() {
        super.onExitAmbient();

        stateTextView.setTextColor(Color.GREEN);
        stateTextView.getPaint().setAntiAlias(true);
    }
    

Atualizar conteúdo no modo ambiente

O modo ambiente permite que você atualize a tela com novas informações para o usuário, mas você precisa equilibrar cuidadosamente as atualizações de tela com a duração da bateria. Considere modificar apenas o método onUpdateAmbient() para atualizar a tela uma vez por minuto no modo ambiente. Caso seu app precise de atualizações mais frequentes, leve em conta que existe uma troca entre a duração da bateria e a frequência de atualizações. Para economizar bateria, as atualizações não precisam acontecer mais do que uma vez a cada 10 segundos. Na prática, no entanto, atualize seu app com menos frequência.

Atualizar uma vez por minuto

Para economizar bateria, a maioria dos apps para Wear não precisa atualizar a tela com frequência no modo ambiente. Recomendamos projetar o app para atualizar a tela uma vez por minuto quando estiver nesse modo. O sistema disponibiliza um método de callback, onUpdateAmbient(), que permite atualizar a tela com essa frequência recomendada.

Para atualizar o conteúdo do seu app, modifique o método onUpdateAmbient() da atividade do wearable:

Kotlin

    override fun onUpdateAmbient() {
        super.onUpdateAmbient()
        // Update the content
    }
    

Java

    @Override
    public void onUpdateAmbient() {
        super.onUpdateAmbient();
        // Update the content
    }
    

Atualizar com mais frequência

É possível, embora não seja recomendado, atualizar um app para Wear mais de uma vez por minuto no modo ambiente. Em apps que exijam atualizações mais frequentes, você pode usar um objeto AlarmManager para ativar o processador e atualizar a tela com mais frequência.

Para implementar um alarme que atualiza o conteúdo com mais frequência no modo ambiente, siga estas etapas:

  1. Prepare o gerenciador de alarmes.
  2. Defina a frequência das atualizações.
  3. Programe a próxima atualização quando a atividade for alternada para o modo ambiente ou estiver no modo ambiente.
  4. Cancele o alarme quando a atividade alternar para o modo interativo ou a atividade for interrompida.

Observação: o gerenciador de alarmes pode criar novas instâncias para sua atividade à medida que são acionadas. Para evitar essa situação, certifique-se de que sua atividade seja declarada com o parâmetro android:launchMode="singleInstance" no manifesto.

As seções a seguir descrevem essas etapas em detalhes.

Preparar o gerenciador de alarmes

O gerenciador de alarmes abre um intent pendente que atualiza a tela e programa o próximo alarme. O exemplo a seguir mostra como declarar o gerenciador de alarmes e o intent pendente no método onCreate() da sua atividade:

Kotlin

    // Action for updating the display in ambient mode, per our custom refresh cycle.
    private const val AMBIENT_UPDATE_ACTION = "com.your.package.action.AMBIENT_UPDATE"
    ...
    private lateinit var ambientUpdateAlarmManager: AlarmManager
    private lateinit var ambientUpdatePendingIntent: PendingIntent
    private lateinit var ambientUpdateBroadcastReceiver: BroadcastReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setAmbientEnabled()

        ambientUpdateAlarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager

        ambientUpdatePendingIntent = Intent(AMBIENT_UPDATE_ACTION).let { ambientUpdateIntent ->
            PendingIntent.getBroadcast(this, 0, ambientUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT)
        }

        ambientUpdateBroadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                refreshDisplayAndSetNextUpdate()
            }
        }
        ...
    }
    

Java

    // Action for updating the display in ambient mode, per our custom refresh cycle.
    private static final String AMBIENT_UPDATE_ACTION = "com.your.package.action.AMBIENT_UPDATE";

    private AlarmManager ambientUpdateAlarmManager;
    private PendingIntent ambientUpdatePendingIntent;
    private BroadcastReceiver ambientUpdateBroadcastReceiver;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setAmbientEnabled();

        ambientUpdateAlarmManager =
            (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        Intent ambientUpdateIntent = new Intent(AMBIENT_UPDATE_ACTION);

        ambientUpdatePendingIntent = PendingIntent.getBroadcast(
            this, 0, ambientUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        ambientUpdateBroadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                refreshDisplayAndSetNextUpdate();
            }
        };
        ...
    }
    

Agora, precisamos registrar e cancelar o broadcast receiver em onResume() e onPause():

Kotlin

    override fun onResume() {
        super.onResume()
        IntentFilter(AMBIENT_UPDATE_ACTION).also { filter ->
            registerReceiver(ambientUpdateBroadcastReceiver, filter)
        }
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(ambientUpdateBroadcastReceiver)
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
    }
    

Java

    @Override
    public void onResume() {
        super.onResume();
        IntentFilter filter = new IntentFilter(AMBIENT_UPDATE_ACTION);
        registerReceiver(ambientUpdateBroadcastReceiver, filter);
            ...
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(ambientUpdateBroadcastReceiver);
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
        ...
    }
    
Atualizar tela e agendar atualizações de dados

Nesta atividade de exemplo, o gerenciador de alarmes é acionado a cada 20 segundos no modo ambiente. Quando o timer é iniciado, o alarme aciona o intent para atualizar a tela e define em quanto tempo ocorrerá a próxima atualização.

O exemplo a seguir mostra como atualizar as informações na tela e definir o alarme para a próxima atualização:

Kotlin

    // Milliseconds between waking processor/screen for updates
    private val AMBIENT_INTERVAL_MS: Long = TimeUnit.SECONDS.toMillis(20)
    ...
    private fun refreshDisplayAndSetNextUpdate() {
        if (isAmbient) {
            // Implement data retrieval and update the screen for ambient mode
        } else {
            // Implement data retrieval and update the screen for interactive mode
        }
        val timeMs: Long = System.currentTimeMillis()
        // Schedule a new alarm
        if (isAmbient) {
            // Calculate the next trigger time
            val delayMs: Long = AMBIENT_INTERVAL_MS - timeMs % AMBIENT_INTERVAL_MS
            val triggerTimeMs: Long = timeMs + delayMs
            ambientUpdateAlarmManager.setExact(
                    AlarmManager.RTC_WAKEUP,
                    triggerTimeMs,
                    ambientUpdatePendingIntent)
        } else {
            // Calculate the next trigger time for interactive mode
        }
    }
    

Java

    // Milliseconds between waking processor/screen for updates
    private static final long AMBIENT_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
    private void refreshDisplayAndSetNextUpdate() {
        if (isAmbient()) {
            // Implement data retrieval and update the screen for ambient mode
        } else {
            // Implement data retrieval and update the screen for interactive mode
        }
        long timeMs = System.currentTimeMillis();
        // Schedule a new alarm
        if (isAmbient()) {
            // Calculate the next trigger time
            long delayMs = AMBIENT_INTERVAL_MS - (timeMs % AMBIENT_INTERVAL_MS);
            long triggerTimeMs = timeMs + delayMs;
            ambientUpdateAlarmManager.setExact(
                AlarmManager.RTC_WAKEUP,
                triggerTimeMs,
                ambientUpdatePendingIntent);
        } else {
            // Calculate the next trigger time for interactive mode
        }
    }
    
Programar o próximo alarme

Programe o alarme para atualizar a tela quando a atividade estiver entrando no modo ambiente ou quando a atividade já estiver nele. Para isso, modifique os métodos onEnterAmbient() e onUpdateAmbient():

Kotlin

    override fun onEnterAmbient(ambientDetails: Bundle?) {
        super.onEnterAmbient(ambientDetails)

        refreshDisplayAndSetNextUpdate()
    }

    override fun onUpdateAmbient() {
        super.onUpdateAmbient()
        refreshDisplayAndSetNextUpdate()
    }
    

Java

    @Override
    public void onEnterAmbient(Bundle ambientDetails) {
        super.onEnterAmbient(ambientDetails);
        refreshDisplayAndSetNextUpdate();
    }

    @Override
    public void onUpdateAmbient() {
        super.onUpdateAmbient();
        refreshDisplayAndSetNextUpdate();
    }
    

Observação: nesse exemplo, o método refreshDisplayAndSetNextUpdate() é chamado sempre que a tela precisa ser atualizada. Para mais exemplos de quando chamar esse método, veja a amostra de AlwaysOn.

Cancelar o alarme

Quando o dispositivo alternar para o modo interativo, cancele o alarme no método onExitAmbient():

Kotlin

    override fun onExitAmbient() {
        super.onExitAmbient()

        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
    }
    

Java

    @Override
    public void onExitAmbient() {
        super.onExitAmbient();
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
    }
    

Quando o usuário sair ou interromper a atividade, cancele o alarme no método onDestroy() da atividade:

Kotlin

    override fun onDestroy() {
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
        super.onDestroy()
    }
    

Java

    @Override
    public void onDestroy() {
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
        super.onDestroy();
    }
    

Manter compatibilidade com versões anteriores

As atividades compatíveis com o modo ambiente retornam automaticamente às atividades normais em dispositivos Wear com versões do Android anteriores à 5.1 (API de nível 22). Nenhum código de app especial é necessário para oferecer compatibilidade com dispositivos dessas versões do Android. Quando o dispositivo alternar para o modo ambiente, retornará para a tela inicial e sairá da atividade.

Caso seu app não precise ser instalado ou atualizado em dispositivos com versões do Android anteriores à 5.1, atualize o manifesto com o seguinte:

    <uses-library android:name="com.google.android.wearable" android:required="true" />