Android 4.3 APIs

Nível da API: 18

O Android 4.3 (JELLY_BEAN_MR2) é uma atualização da versão Jelly Bean que oferece novos recursos para usuários e desenvolvedores de apps. Este documento apresenta uma introdução às novas APIs mais importantes.

Como desenvolvedor de apps, faça o download da imagem do sistema do Android 4.3 e da plataforma do SDK no SDK Manager assim que possível. Se você não tiver um dispositivo com o Android 4.3 para testar o app, use a imagem do sistema do Android 4.3 para testar o app no Android Emulator. Em seguida, crie seus apps na plataforma Android 4.3 para começar a usar as APIs mais recentes.

Atualizar o nível desejado da API

Para otimizar melhor seu app para dispositivos com o Android 4.3, defina o targetSdkVersion como "18", instale-o em uma imagem do sistema Android 4.3, teste-o e publique uma atualização com essa mudança.

É possível usar APIs no Android 4.3 e, ao mesmo tempo, oferecer suporte a versões mais antigas adicionando condições ao código que verificam o nível da API do sistema antes de executar APIs não compatíveis com seu minSdkVersion. Para saber mais sobre como manter a compatibilidade com versões anteriores, leia Suporte a diferentes versões da plataforma.

Várias APIs também estão disponíveis na Biblioteca de Suporte do Android, que permite implementar novos recursos em versões mais antigas da plataforma.

Para mais informações sobre como os níveis da API funcionam, leia O que é o nível da API?.

Mudanças importantes de comportamento

Se você já publicou um app para Android, saiba que ele pode ser afetado pelas mudanças no Android 4.3.

Se o app usa intents implícitas…

Seu app pode se comportar de maneira inadequada em um ambiente de perfil restrito.

Os usuários em um ambiente de perfil restrito podem não ter todos os apps Android padrão disponíveis. Por exemplo, um perfil restrito pode ter o navegador da Web e o app de câmera desativados. Portanto, seu app não deve presumir quais apps estão disponíveis, porque, se você chamar startActivity() sem verificar se um app está disponível para processar a Intent, seu app poderá falhar em um perfil restrito.

Ao usar uma intent implícita, sempre verifique se um app está disponível para processar a intent chamando resolveActivity() ou queryIntentActivities(). Exemplo:

Kotlin

val intent = Intent(Intent.ACTION_SEND)
...
if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
} else {
    Toast.makeText(context, R.string.app_not_available, Toast.LENGTH_LONG).show()
}

Java

Intent intent = new Intent(Intent.ACTION_SEND);
...
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
} else {
    Toast.makeText(context, R.string.app_not_available, Toast.LENGTH_LONG).show();
}

Se o app depende de contas…

Seu app pode se comportar de maneira inadequada em um ambiente de perfil restrito.

Os usuários em um ambiente de perfil restrito não têm acesso às contas de usuário por padrão. Se o app depender de um Account, ele poderá falhar ou se comportar de maneira inesperada quando usado em um perfil restrito.

Se você quiser impedir que perfis restritos usem seu app porque ele depende de informações de conta confidenciais, especifique o atributo android:requiredAccountType no elemento <application> do manifesto.

Se você quiser permitir que perfis restritos continuem usando seu app, mesmo que não possam criar as próprias contas, desative os recursos do app que exigem uma conta ou permita que perfis restritos acessem as contas criadas pelo usuário principal. Para mais informações, consulte a seção abaixo sobre Como oferecer suporte a contas em um perfil restrito.

Se o app usa VideoView…

Seu vídeo pode parecer menor no Android 4.3.

Em versões anteriores do Android, o widget VideoView calculava incorretamente o valor de "wrap_content" para layout_height e layout_width como sendo o mesmo que "match_parent". Portanto, embora o uso de "wrap_content" para a altura ou largura possa ter fornecido o layout de vídeo desejado, isso pode resultar em um vídeo muito menor no Android 4.3 e versões mais recentes. Para corrigir o problema, substitua "wrap_content" por "match_parent" e verifique se o vídeo aparece como esperado no Android 4.3 e em versões anteriores.

Perfis restritos

Em tablets Android, agora é possível criar perfis restritos com base no usuário principal. Ao criar um perfil restrito, os usuários podem ativar restrições, como quais apps estão disponíveis para o perfil. Um novo conjunto de APIs no Android 4.3 também permite criar configurações de restrição detalhadas para os apps que você desenvolve. Por exemplo, ao usar as novas APIs, você pode permitir que os usuários controlem o tipo de conteúdo disponível no app quando ele é executado em um ambiente de perfil restrito.

A interface para que os usuários controlem as restrições que você criou é gerenciada pelo app Configurações do sistema. Para que as configurações de restrição do app apareçam para o usuário, declare as restrições fornecidas pelo app criando um BroadcastReceiver que receba a intent ACTION_GET_RESTRICTION_ENTRIES. O sistema invoca essa intent para consultar as restrições disponíveis em todos os apps e, em seguida, cria a interface para permitir que o usuário principal gerencie as restrições de cada perfil restrito.

No método onReceive() do BroadcastReceiver, é preciso criar um RestrictionEntry para cada restrição fornecida pelo app. Cada RestrictionEntry define um título, uma descrição e um dos seguintes tipos de dados:

  • TYPE_BOOLEAN para uma restrição que é verdadeira ou falsa.
  • TYPE_CHOICE para uma restrição que tem várias opções mutuamente exclusivas (opções de botão de opção).
  • TYPE_MULTI_SELECT para uma restrição que tem várias opções que não são mutuamente exclusivas (opções de caixa de seleção).

Em seguida, coloque todos os objetos RestrictionEntry em um ArrayList e coloque-o no resultado do broadcast receiver como o valor do extra EXTRA_RESTRICTIONS_LIST.

O sistema cria a IU para as restrições do app no app Configurações e salva cada restrição com a chave exclusiva que você forneceu para cada objeto RestrictionEntry. Quando o usuário abrir o app, você poderá consultar as restrições atuais chamando getApplicationRestrictions(). Isso retorna um Bundle que contém os pares de chave-valor para cada restrição definida com os objetos RestrictionEntry.

Se você quiser fornecer restrições mais específicas que não podem ser processadas por valores booleanos, de escolha única e de múltipla escolha, crie uma atividade em que o usuário possa especificar as restrições e permita que os usuários abram essa atividade nas configurações de restrição. No broadcast receiver, inclua o extra EXTRA_RESTRICTIONS_INTENT no resultado Bundle. Esse extra precisa especificar um Intent indicando a classe Activity a ser iniciada. Use o método putParcelable() para transmitir EXTRA_RESTRICTIONS_INTENT com a intent. Quando o usuário principal entra na sua atividade para definir restrições personalizadas, ela precisa retornar um resultado que contenha os valores de restrição em um extra usando a chave EXTRA_RESTRICTIONS_LIST ou EXTRA_RESTRICTIONS_BUNDLE, dependendo se você especificar objetos RestrictionEntry ou pares de chave-valor, respectivamente.

Contas compatíveis em um perfil restrito

Todas as contas adicionadas ao usuário principal estão disponíveis para um perfil restrito, mas as contas não podem ser acessadas pelas APIs AccountManager por padrão. Se você tentar adicionar uma conta com AccountManager em um perfil restrito, vai receber um resultado de falha. Devido a essas restrições, você tem as três opções a seguir:

  • Permitir o acesso às contas do proprietário em um perfil restrito.

    Para acessar uma conta de um perfil restrito, adicione o atributo android:restrictedAccountType à tag <application>:

    <application ...
        android:restrictedAccountType="com.example.account.type" >

    Atenção: ativar esse atributo permite que o app acesse as contas do usuário principal em perfis restritos. Portanto, permita isso somente se as informações exibidas pelo app não revelarem informações de identificação pessoal (PII, na sigla em inglês) consideradas sensíveis. As configurações do sistema vão informar ao usuário principal que seu app concede perfis restritos às contas dele. Portanto, deve ficar claro para o usuário que o acesso à conta é importante para a funcionalidade do app. Se possível, também forneça controles de restrição adequados para o usuário principal que definam quanto acesso à conta é permitido no app.

  • Desative algumas funcionalidades quando não for possível modificar contas.

    Se você quiser usar contas, mas elas não forem necessárias para a funcionalidade principal do app, verifique a disponibilidade da conta e desative os recursos quando ela não estiver disponível. Primeiro, verifique se há uma conta disponível. Caso contrário, consulte se é possível criar uma nova conta chamando getUserRestrictions() e verifique o extra DISALLOW_MODIFY_ACCOUNTS no resultado. Se for true, desative qualquer funcionalidade do app que exija acesso a contas. Exemplo:

    Kotlin

    val um = context.getSystemService(Context.USER_SERVICE) as UserManager
    val restrictions: Bundle = um.userRestrictions
    if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
        // cannot add accounts, disable some functionality
    }

    Java

    UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    Bundle restrictions = um.getUserRestrictions();
    if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
        // cannot add accounts, disable some functionality
    }

    Observação: nesse cenário, não declare novos atributos no arquivo de manifesto.

  • Desative o app quando não for possível acessar contas particulares.

    Se for importante que o app não esteja disponível para perfis restritos porque ele depende de informações pessoais sensíveis em uma conta (e porque perfis restritos não podem adicionar novas contas no momento), adicione o atributo android:requiredAccountType à tag <application>:

    <application ...
        android:requiredAccountType="com.example.account.type" >

    Por exemplo, o app Gmail usa esse atributo para se desativar em perfis restritos, porque o e-mail pessoal do proprietário não pode estar disponível para perfis restritos.

  • Redes sem fio e conectividade

    Bluetooth de baixa energia (Smart Ready)

    O Android agora oferece suporte ao Bluetooth de baixa energia (LE) com novas APIs em android.bluetooth. Com as novas APIs, é possível criar apps Android que se comunicam com periféricos Bluetooth de baixa energia, como monitores de frequência cardíaca e pedômetros.

    Como o Bluetooth LE é um recurso de hardware que não está disponível em todos os dispositivos Android, é necessário declarar no arquivo de manifesto um elemento <uses-feature> para "android.hardware.bluetooth_le":

    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

    Se você já conhece as APIs Bluetooth clássicas do Android, observe que o uso das APIs Bluetooth LE tem algumas diferenças. O mais importante é que agora há uma classe BluetoothManager que você precisa usar para algumas operações de alto nível, como adquirir um BluetoothAdapter, receber uma lista de dispositivos conectados e verificar o estado de um dispositivo. Por exemplo, veja como receber o BluetoothAdapter:

    Kotlin

    val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    bluetoothAdapter = bluetoothManager.adapter

    Java

    final BluetoothManager bluetoothManager =
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    bluetoothAdapter = bluetoothManager.getAdapter();

    Para descobrir periféricos Bluetooth LE, chame startLeScan() no BluetoothAdapter, transmitindo uma implementação da interface BluetoothAdapter.LeScanCallback. Quando o adaptador Bluetooth detecta um periférico Bluetooth LE, sua implementação de BluetoothAdapter.LeScanCallback recebe uma chamada para o método onLeScan(). Esse método fornece um objeto BluetoothDevice que representa o dispositivo detectado, o valor de RSSI do dispositivo e uma matriz de bytes que contém o registro de anúncio do dispositivo.

    Se você quiser procurar apenas tipos específicos de periféricos, chame startLeScan() e inclua uma matriz de objetos UUID que especificam os serviços GATT compatíveis com o app.

    Observação: só é possível buscar dispositivos Bluetooth LE ou buscar dispositivos Bluetooth clássicos usando APIs anteriores. Não é possível buscar dispositivos Bluetooth LE e Classic ao mesmo tempo.

    Para se conectar a um periférico Bluetooth LE, chame connectGatt() no objeto BluetoothDevice correspondente, transmitindo uma implementação de BluetoothGattCallback. Sua implementação de BluetoothGattCallback recebe callbacks sobre o estado de conectividade com o dispositivo e outros eventos. É durante o callback onConnectionStateChange() que você pode começar a se comunicar com o dispositivo se o método transmitir STATE_CONNECTED como o novo estado.

    Para acessar os recursos de Bluetooth em um dispositivo, seu app também precisa solicitar determinadas permissões de usuário do Bluetooth. Para mais informações, consulte o guia da API Bluetooth de baixa energia.

    Modo somente leitura de Wi-Fi

    Ao tentar identificar a localização do usuário, o Android pode usar o Wi-Fi para ajudar a determinar o local, verificando os pontos de acesso por perto. No entanto, os usuários costumam manter o Wi-Fi desativado para economizar bateria, o que resulta em dados de local menos precisos. O Android agora inclui um modo somente de verificação que permite que o Wi-Fi do dispositivo verifique pontos de acesso para ajudar a obter a localização sem se conectar a um ponto de acesso, reduzindo significativamente o uso da bateria.

    Se você quiser adquirir a localização do usuário, mas o Wi-Fi estiver desativado, peça para o usuário ativar o modo de somente busca por Wi-Fi chamando startActivity() com a ação ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE.

    Configuração do Wi-Fi

    As novas APIs WifiEnterpriseConfig permitem que serviços voltados para empresas automatizem a configuração de Wi-Fi para dispositivos gerenciados.

    Resposta rápida para chamadas recebidas

    Desde o Android 4.0, um recurso chamado "Resposta rápida" permite que os usuários respondam a chamadas recebidas com uma mensagem de texto imediata, sem precisar atender à chamada ou desbloquear o dispositivo. Até agora, essas mensagens rápidas eram sempre processadas pelo app de mensagens padrão. Agora, qualquer app pode declarar a capacidade de processar essas mensagens criando um Service com um filtro de intent para ACTION_RESPOND_VIA_MESSAGE.

    Quando o usuário responde a uma chamada recebida com uma resposta rápida, o app Telefone envia a intent ACTION_RESPOND_VIA_MESSAGE com um URI que descreve o destinatário (o autor da chamada) e o extra EXTRA_TEXT com a mensagem que o usuário quer enviar. Quando o serviço recebe a intent, ele precisa entregar a mensagem e ser interrompido imediatamente (o app não deve mostrar uma atividade).

    Para receber essa intent, é necessário declarar a permissão SEND_RESPOND_VIA_MESSAGE.

    Multimídia

    Melhorias no MediaExtractor e no MediaCodec

    Agora, o Android facilita a criação de players de Dynamic Adaptive Streaming over HTTP (DASH) de acordo com o padrão ISO/IEC 23009-1, usando as APIs atuais em MediaCodec e MediaExtractor. O framework que serve de base para essas APIs foi atualizado para oferecer suporte à análise de arquivos MP4 fragmentados, mas o app ainda é responsável por analisar os metadados do MPD e transmitir os streams individuais para MediaExtractor.

    Se você quiser usar o DASH com conteúdo criptografado, observe que o método getSampleCryptoInfo() retorna os metadados MediaCodec.CryptoInfo que descrevem a estrutura de cada amostra de mídia criptografada. Além disso, o método getPsshInfo() foi adicionado a MediaExtractor para que você possa acessar os metadados PSSH da sua mídia DASH. Esse método retorna um mapa de objetos UUID para bytes, com o UUID especificando o esquema de criptografia e os bytes sendo os dados específicos desse esquema.

    DRM de mídia

    A nova classe MediaDrm oferece uma solução modular para o gerenciamento de direitos digitais (DRM) com seu conteúdo de mídia, separando as questões de DRM da reprodução de mídia. Por exemplo, essa separação de API permite que você reproduza conteúdo criptografado pelo Widevine sem precisar usar o formato de mídia do Widevine. Essa solução de DRM também é compatível com a criptografia comum do DASH, para que você possa usar vários esquemas de DRM com seu conteúdo de streaming.

    É possível usar MediaDrm para receber mensagens opacas de solicitação de chave e processar mensagens de resposta de chave do servidor para aquisição e provisionamento de licenças. Seu app é responsável por processar a comunicação de rede com os servidores. A classe MediaDrm oferece apenas a capacidade de gerar e processar as mensagens.

    As APIs MediaDrm foram criadas para serem usadas em conjunto com as APIs MediaCodec, que foram introduzidas no Android 4.1 (nível 16 da API), incluindo MediaCodec para codificar e decodificar seu conteúdo, MediaCrypto para processar conteúdo criptografado e MediaExtractor para extrair e demultiplexar seu conteúdo.

    Primeiro, é preciso criar os objetos MediaExtractor e MediaCodec. Em seguida, é possível acessar o UUID que identifica o esquema de DRM, normalmente nos metadados do conteúdo, e usá-lo para criar uma instância de um objeto MediaDrm com o construtor dele.

    Codificação de vídeo de uma Surface

    O Android 4.1 (nível 16 da API) adicionou a classe MediaCodec para codificação e decodificação de baixo nível de conteúdo de mídia. Ao codificar vídeo, o Android 4.1 exigia que você fornecesse à mídia uma matriz ByteBuffer, mas o Android 4.3 agora permite usar um Surface como entrada para um codificador. Por exemplo, isso permite codificar a entrada de um arquivo de vídeo ou usar frames gerados pelo OpenGL ES.

    Para usar um Surface como entrada para o codificador, primeiro chame configure() para o MediaCodec. Em seguida, chame createInputSurface() para receber o Surface em que você pode transmitir sua mídia.

    Por exemplo, é possível usar o Surface fornecido como a janela para um contexto do OpenGL, transmitindo-o para eglCreateWindowSurface(). Em seguida, ao renderizar a superfície, chame eglSwapBuffers() para transmitir o frame para o MediaCodec.

    Para iniciar a codificação, chame start() no MediaCodec. Quando terminar, chame signalEndOfInputStream() para encerrar a codificação e chame release() no Surface.

    Multiplexação de mídia

    A nova classe MediaMuxer permite a multiplexação entre um stream de áudio e um stream de vídeo. Essas APIs servem como uma contraparte da classe MediaExtractor, adicionada no Android 4.2 para desmultiplexação (demuxing) de mídia.

    Os formatos de saída compatíveis são definidos em MediaMuxer.OutputFormat. No momento, o MP4 é o único formato de saída compatível, e o MediaMuxer aceita apenas um stream de áudio e/ou um stream de vídeo por vez.

    O MediaMuxer foi projetado principalmente para funcionar com o MediaCodec, para que você possa realizar o processamento de vídeo pelo MediaCodec e salvar a saída em um arquivo MP4 pelo MediaMuxer. Também é possível usar MediaMuxer em combinação com MediaExtractor para realizar edições de mídia sem precisar codificar ou decodificar.

    Progresso da reprodução e limpeza para RemoteControlClient

    No Android 4.0 (nível 14 da API), o RemoteControlClient foi adicionado para permitir controles de mídia de clientes de controle remoto, como os controles disponíveis na tela de bloqueio. O Android 4.3 agora permite que esses controles mostrem a posição de reprodução e os controles para arrastar a reprodução. Se você ativou o controle remoto para seu app de música com as APIs RemoteControlClient, é possível permitir a limpeza da reprodução implementando duas novas interfaces.

    Primeiro, é preciso ativar a flag FLAG_KEY_MEDIA_POSITION_UPDATE transmitindo-a para setTransportControlsFlags().

    Em seguida, implemente as duas novas interfaces:

    RemoteControlClient.OnGetPlaybackPositionListener
    Isso inclui o callback onGetPlaybackPosition(), que solicita a posição atual da mídia quando o controle remoto precisa atualizar o progresso na interface.
    RemoteControlClient.OnPlaybackPositionUpdateListener
    Isso inclui o callback onPlaybackPositionUpdate(), que informa ao app o novo código de tempo da mídia quando o usuário arrasta a reprodução com a interface do controle remoto.

    Depois de atualizar a reprodução com a nova posição, chame setPlaybackState() para indicar o novo estado, posição e velocidade de reprodução.

    Com essas interfaces definidas, você pode defini-las para o RemoteControlClient chamando setOnGetPlaybackPositionListener() e setPlaybackPositionUpdateListener(), respectivamente.

    Gráficos

    Suporte para OpenGL ES 3.0

    O Android 4.3 adiciona interfaces Java e suporte nativo para o OpenGL ES 3.0. As principais novas funcionalidades fornecidas no OpenGL ES 3.0 incluem:

    • Aceleração de efeitos visuais avançados
    • Compactação de textura ETC2/EAC de alta qualidade como um recurso padrão
    • Uma nova versão da linguagem de sombreamento GLSL ES com suporte a números inteiros e pontos flutuantes de 32 bits.
    • Renderização avançada de texturas
    • Padronização mais ampla do tamanho da textura e dos formatos de buffer de renderização

    A interface Java para OpenGL ES 3.0 no Android é fornecida com GLES30. Ao usar o OpenGL ES 3.0, declare-o no arquivo de manifesto com a tag <uses-feature> e o atributo android:glEsVersion. Exemplo:

    <manifest>
        <uses-feature android:glEsVersion="0x00030000" />
        ...
    </manifest>

    E lembre-se de especificar o contexto do OpenGL ES chamando setEGLContextClientVersion(), transmitindo 3 como a versão.

    Para mais informações sobre como usar o OpenGL ES, inclusive como conferir a versão do OpenGL ES compatível no tempo de execução, consulte o guia da API do OpenGL ES.

    Mipmapping para drawables

    Usar um mipmap como origem para o bitmap ou drawable é uma maneira simples de fornecer uma imagem de qualidade e várias escalas de imagem, o que pode ser particularmente útil se você espera que a imagem seja dimensionada durante uma animação.

    O Android 4.2 (nível 17 da API) adicionou suporte a mipmaps na classe Bitmap. O Android troca as imagens mip no Bitmap quando você fornece uma origem de mipmap e ativa setHasMipMap(). No Android 4.3, também é possível ativar mipmaps para um objeto BitmapDrawable fornecendo um recurso de mipmap e definindo o atributo android:mipMap em um arquivo de recurso de bitmap ou chamando hasMipMap().

    Interface do usuário

    Ver sobreposições

    A nova classe ViewOverlay fornece uma camada transparente sobre um View em que é possível adicionar conteúdo visual e que não afeta a hierarquia de layout. Para receber um ViewOverlay para qualquer View, chame getOverlay(). A sobreposição sempre tem o mesmo tamanho e posição que a visualização do host (a visualização em que foi criada), permitindo que você adicione conteúdo que aparece na frente da visualização do host, mas que não pode estender os limites dessa visualização.

    Usar um ViewOverlay é útil quando você quer criar animações, como deslizar uma visualização para fora do contêiner ou mover itens pela tela sem afetar a hierarquia de visualização. No entanto, como a área utilizável de uma sobreposição é restrita à mesma área da visualização do host, se você quiser animar uma visualização que se move para fora da posição no layout, use uma sobreposição de uma visualização da família que tenha os limites de layout desejados.

    Ao criar uma sobreposição para uma visualização de widget, como um Button, é possível adicionar objetos Drawable à sobreposição chamando add(Drawable). Se você chamar getOverlay() para uma visualização de layout, como RelativeLayout, o objeto retornado será um ViewGroupOverlay. A classe ViewGroupOverlay é uma subclasse de ViewOverlay que também permite adicionar objetos View chamando add(View).

    Observação: todos os drawables e visualizações adicionados a uma sobreposição são apenas visuais. Eles não podem receber eventos de foco ou entrada.

    Por exemplo, o código abaixo anima uma visualização deslizando para a direita, colocando-a na sobreposição da visualização da família e, em seguida, realizando uma animação de translação nessa visualização:

    Kotlin

    val view: View? = findViewById(R.id.view_to_remove)
    val container: ViewGroup? = view?.parent as ViewGroup
    
    container?.apply {
        overlay.add(view)
        ObjectAnimator.ofFloat(view, "translationX", right.toFloat())
                .start()
    }

    Java

    View view = findViewById(R.id.view_to_remove);
    ViewGroup container = (ViewGroup) view.getParent();
    container.getOverlay().add(view);
    ObjectAnimator anim = ObjectAnimator.ofFloat(view, "translationX", container.getRight());
    anim.start();

    Layout de limites ópticos

    Para visualizações que contêm imagens de plano de fundo de nove patches, agora é possível especificar que elas sejam alinhadas com as visualizações vizinhas com base nos limites "ópticos" da imagem de plano de fundo, em vez dos limites de "clipe" da visualização.

    Por exemplo, as figuras 1 e 2 mostram o mesmo layout, mas a versão na figura 1 está usando limites de corte (o comportamento padrão), enquanto a figura 2 está usando limites ópticos. Como as imagens de nove patches usadas para o botão e o frame de fotos incluem padding nas bordas, elas não parecem se alinhar entre si ou com o texto ao usar limites de corte.

    Observação: as capturas de tela nas figuras 1 e 2 têm a configuração para desenvolvedores "Mostrar limites de layout" ativada. Para cada visualização, as linhas vermelhas indicam os limites ópticos, as linhas azuis indicam os limites de clipe e o rosa indica as margens.

    Figura 1. Layout usando limites de corte (padrão).

    Figura 2. Layout usando limites ópticos.

    Para alinhar as visualizações com base nos limites ópticos, defina o atributo android:layoutMode como "opticalBounds" em um dos layouts pai. Exemplo:

    <LinearLayout android:layoutMode="opticalBounds" ... >

    Figura 3. Visualização ampliada do Nine-Patch do botão Holo com limites ópticos.

    Para que isso funcione, as imagens nine-patch aplicadas ao plano de fundo das visualizações precisam especificar os limites ópticos usando linhas vermelhas na parte de baixo e no lado direito do arquivo nine-patch, como mostrado na Figura 3. As linhas vermelhas indicam a região que precisa ser subtraída dos limites de corte, deixando os limites ópticos da imagem.

    Quando você ativa os limites ópticos para um ViewGroup no layout, todas as visualizações descendentes herdam o modo de layout de limites ópticos, a menos que você o substitua para um grupo definindo android:layoutMode como "clipBounds". Todos os elementos de layout também respeitam os limites ópticos das visualizações filhas, adaptando os próprios limites com base nos limites ópticos das visualizações dentro deles. No entanto, os elementos de layout (subclasses de ViewGroup) atualmente não são compatíveis com limites ópticos para imagens de nove patches aplicadas ao próprio plano de fundo.

    Se você criar uma visualização personalizada ao criar uma subclasse de View, ViewGroup ou qualquer subclasse delas, sua visualização vai herdar esses comportamentos de limites ópticos.

    Observação: todos os widgets compatíveis com o tema Holo foram atualizados com limites ópticos, incluindo Button, Spinner, EditText e outros. Portanto, você pode se beneficiar imediatamente ao definir o atributo android:layoutMode como "opticalBounds" se o app aplicar um tema Holo (Theme.Holo, Theme.Holo.Light etc.).

    Para especificar limites ópticos para suas próprias imagens nine-patch com a ferramenta Draw 9-patch, mantenha a tecla Control pressionada ao clicar nos pixels da borda.

    Animação para valores Rect

    Agora é possível animar entre dois valores Rect com o novo RectEvaluator. Essa nova classe é uma implementação de TypeEvaluator que pode ser transmitida para ValueAnimator.setEvaluator().

    Listener de anexação e foco de janela

    Anteriormente, se você quisesse detectar quando a visualização era anexada/desanexada da janela ou quando o foco mudava, era necessário substituir a classe View para implementar onAttachedToWindow() e onDetachedFromWindow() ou onWindowFocusChanged(), respectivamente.

    Agora, para receber eventos de anexação e desanexação, você pode implementar ViewTreeObserver.OnWindowAttachListener e definir em uma visualização com addOnWindowAttachListener(). Para receber eventos de foco, implemente ViewTreeObserver.OnWindowFocusChangeListener e defina-o em uma visualização com addOnWindowFocusChangeListener().

    Suporte a overscan de TV

    Para garantir que o app preencha toda a tela em todas as TVs, agora é possível ativar o overscan no layout do app. O modo de overscan é determinado pela flag FLAG_LAYOUT_IN_OVERSCAN, que pode ser ativada com temas de plataforma, como Theme_DeviceDefault_NoActionBar_Overscan, ou ativando o estilo windowOverscan em um tema personalizado.

    Orientação da tela

    O atributo screenOrientation da tag <activity> agora aceita outros valores para respeitar a preferência do usuário pela rotação automática:

    "userLandscape"
    Se comporta da mesma forma que "sensorLandscape", exceto se o usuário desativar a rotação automática. Nesse caso, ele é bloqueado na orientação de paisagem normal e não é invertido.
    "userPortrait"
    Se comporta da mesma forma que "sensorPortrait", exceto se o usuário desativar a rotação automática. Nesse caso, ele é bloqueado na orientação retrato normal e não gira.
    "fullUser"
    Tem o mesmo comportamento que "fullSensor" e permite a rotação em todas as quatro direções, exceto se o usuário desativar a rotação automática. Nesse caso, ele bloqueia na orientação preferida do usuário.

    Além disso, agora você também pode declarar "locked" para bloquear a orientação do app na orientação atual da tela.

    Animações de rotação

    O novo campo rotationAnimation em WindowManager permite que você selecione uma das três animações que quer usar quando o sistema alternar as orientações da tela. As três animações são:

    Observação: essas animações só vão estar disponíveis se você tiver definido a atividade para usar o modo de tela cheia, que pode ser ativado com temas como Theme.Holo.NoActionBar.Fullscreen.

    Por exemplo, veja como ativar a animação de "crossfade":

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        val params: WindowManager.LayoutParams = window.attributes
        params.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE
        window.attributes = params
        ...
    }

    Java

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        WindowManager.LayoutParams params = getWindow().getAttributes();
        params.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
        getWindow().setAttributes(params);
        ...
    }

    Opinião dos usuários

    Novos tipos de sensor

    O novo sensor TYPE_GAME_ROTATION_VECTOR permite detectar as rotações do dispositivo sem se preocupar com interferências magnéticas. Ao contrário do sensor TYPE_ROTATION_VECTOR, o TYPE_GAME_ROTATION_VECTOR não se baseia no norte magnético.

    Os novos sensores TYPE_GYROSCOPE_UNCALIBRATED e TYPE_MAGNETIC_FIELD_UNCALIBRATED fornecem dados do sensor brutos sem considerar as estimativas de viés. Ou seja, os sensores TYPE_GYROSCOPE e TYPE_MAGNETIC_FIELD fornecem dados do sensor que consideram o desvio estimado de desvio de giroscópio e ferro duro no dispositivo, respectivamente. Já as novas versões "não calibradas" desses sensores fornecem os dados brutos do sensor e oferecem os valores de polarização estimados separadamente. Esses sensores permitem que você forneça sua própria calibração personalizada para os dados do sensor, melhorando a polarização estimada com dados externos.

    Listener de notificações

    O Android 4.3 adiciona uma nova classe de serviço, NotificationListenerService, que permite que seu app receba informações sobre novas notificações à medida que são postadas pelo sistema.

    Se o app usa as APIs do serviço de acessibilidade para acessar notificações do sistema, atualize-o para usar essas APIs.

    Provedor de contatos

    Consultar "contactables"

    A nova consulta do provedor de contatos, Contactables.CONTENT_URI, oferece uma maneira eficiente de receber um Cursor que contém todos os endereços de e-mail e números de telefone de todos os contatos que correspondem à consulta especificada.

    Consultar deltas de contatos

    Novas APIs foram adicionadas ao Provedor de contatos para que você possa consultar com eficiência as mudanças recentes nos dados de contatos. Antes, seu app podia ser notificado quando algo nos dados de contatos mudava, mas você não sabia exatamente o que tinha mudado e precisava recuperar todos os contatos e iterar por eles para descobrir a mudança.

    Para acompanhar as mudanças em inserções e atualizações, agora é possível incluir o parâmetro CONTACT_LAST_UPDATED_TIMESTAMP na seleção para consultar apenas os contatos que mudaram desde a última consulta ao provedor.

    Para rastrear quais contatos foram excluídos, a nova tabela ContactsContract.DeletedContacts fornece um registro de contatos que foram excluídos (mas cada contato excluído é mantido nessa tabela por um tempo limitado). Assim como em CONTACT_LAST_UPDATED_TIMESTAMP, você pode usar o novo parâmetro de seleção CONTACT_DELETED_TIMESTAMP para verificar quais contatos foram excluídos desde a última vez que você consultou o provedor. A tabela também contém a constante DAYS_KEPT_MILLISECONDS, que indica o número de dias (em milissegundos) em que o registro será mantido.

    Além disso, o Provedor de contatos agora transmite a ação CONTACTS_DATABASE_CREATED quando o usuário limpa o armazenamento de contatos pelo menu de configurações do sistema, recriando o banco de dados do Provedor de contatos. Ele serve para sinalizar aos apps que eles precisam descartar todas as informações de contato armazenadas e recarregá-las com uma nova consulta.

    Para ver um exemplo de código que usa essas APIs para verificar mudanças nos contatos, consulte o exemplo ApiDemos disponível no download de Exemplos do SDK.

    Localização

    Melhoria no suporte para texto bidirecional

    As versões anteriores do Android são compatíveis com idiomas e layouts da direita para a esquerda (RTL, na sigla em inglês), mas às vezes não processam corretamente textos com direções mistas. Por isso, o Android 4.3 adiciona as APIs BidiFormatter, que ajudam a formatar corretamente o texto com conteúdo de direção oposta sem distorcer nenhuma parte dele.

    Por exemplo, quando você quiser criar uma frase com uma variável de string, como "Você quis dizer Rua das Flores, 15, São Paulo, SP?", normalmente é transmitido um recurso de string localizado e a variável para String.format():

    Kotlin

    val suggestion = String.format(resources.getString(R.string.did_you_mean), address)

    Java

    Resources res = getResources();
    String suggestion = String.format(res.getString(R.string.did_you_mean), address);

    No entanto, se a localidade for o hebraico, a string formatada será assim:

    האם התכוונת ל 15 Bay Street, Laurel, CA?

    Isso está errado porque o "15" deveria estar à esquerda de "Bay Street". A solução é usar BidiFormatter e o método unicodeWrap(). Por exemplo, o código acima se torna:

    Kotlin

    val bidiFormatter = BidiFormatter.getInstance()
    val suggestion = String.format(
            resources.getString(R.string.did_you_mean),
            bidiFormatter.unicodeWrap(address)
    )

    Java

    Resources res = getResources();
    BidiFormatter bidiFormatter = BidiFormatter.getInstance();
    String suggestion = String.format(res.getString(R.string.did_you_mean),
            bidiFormatter.unicodeWrap(address));

    Por padrão, unicodeWrap() usa a heurística de estimativa de direcionalidade first-strong, que pode errar se o primeiro sinal de direção do texto não representar a direção apropriada para o conteúdo como um todo. Se necessário, é possível especificar uma heurística diferente transmitindo uma das constantes TextDirectionHeuristic de TextDirectionHeuristics para unicodeWrap().

    Observação: essas novas APIs também estão disponíveis para versões anteriores do Android pela Biblioteca de Suporte do Android, com a classe BidiFormatter e as APIs relacionadas.

    Serviços de acessibilidade

    Gerenciar eventos de teclas

    Agora, uma AccessibilityService pode receber um callback para eventos de entrada de tecla com o método de callback onKeyEvent(). Isso permite que o serviço de acessibilidade processe entradas de dispositivos de entrada baseados em teclas, como um teclado, e traduza esses eventos em ações especiais que antes só eram possíveis com entrada por toque ou o botão direcional do dispositivo.

    Selecionar texto e copiar/colar

    O AccessibilityNodeInfo agora fornece APIs que permitem que um AccessibilityService selecione, recorte, copie e cole texto em um nó.

    Para especificar a seleção de texto a ser recortado ou copiado, o serviço de acessibilidade pode usar a nova ação, ACTION_SET_SELECTION, transmitindo com ela a posição inicial e final da seleção com ACTION_ARGUMENT_SELECTION_START_INT e ACTION_ARGUMENT_SELECTION_END_INT. Como alternativa, é possível selecionar o texto manipulando a posição do cursor usando a ação existente, ACTION_NEXT_AT_MOVEMENT_GRANULARITY (anteriormente apenas para mover a posição do cursor), e adicionando o argumento ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN.

    Em seguida, você pode cortar ou copiar com ACTION_CUT, ACTION_COPY e depois colar com ACTION_PASTE.

    Observação: essas novas APIs também estão disponíveis para versões anteriores do Android pela Biblioteca de Suporte do Android, com a classe AccessibilityNodeInfoCompat.

    Declarar recursos de acessibilidade

    A partir do Android 4.3, um serviço de acessibilidade precisa declarar recursos de acessibilidade no arquivo de metadados para usar determinados recursos de acessibilidade. Se o recurso não for solicitado no arquivo de metadados, o recurso não será operacional. Para declarar os recursos de acessibilidade do serviço, use atributos XML que correspondam às várias constantes de "recurso" na classe AccessibilityServiceInfo.

    Por exemplo, se um serviço não solicitar o recurso flagRequestFilterKeyEvents, ele não vai receber eventos de tecla.

    Como testar e depurar

    Testes automatizados de interface

    A nova classe UiAutomation fornece APIs que permitem simular ações do usuário para automação de testes. Ao usar as APIs AccessibilityService da plataforma, as APIs UiAutomation permitem inspecionar o conteúdo da tela e injetar eventos arbitrários de teclado e toque.

    Para receber uma instância de UiAutomation, chame Instrumentation.getUiAutomation(). Para que isso funcione, forneça a opção -w com o comando instrument ao executar o InstrumentationTestCase do adb shell.

    Com a instância UiAutomation, é possível executar eventos arbitrários para testar seu app chamando executeAndWaitForEvent(), transmitindo um Runnable para execução, um período de tempo limite para a operação e uma implementação da interface UiAutomation.AccessibilityEventFilter. É na implementação de UiAutomation.AccessibilityEventFilter que você vai receber uma chamada que permite filtrar os eventos de seu interesse e determinar o sucesso ou a falha de um determinado caso de teste.

    Para observar todos os eventos durante um teste, crie uma implementação de UiAutomation.OnAccessibilityEventListener e transmita-a para setOnAccessibilityEventListener(). A interface do listener recebe uma chamada para onAccessibilityEvent() sempre que um evento ocorre, recebendo um objeto AccessibilityEvent que descreve o evento.

    Há uma variedade de outras operações que as APIs UiAutomation expõem em um nível muito baixo para incentivar o desenvolvimento de ferramentas de teste de interface, como o uiautomator. Por exemplo, UiAutomation também pode:

    • Injetar eventos de entrada
    • Mudar a orientação da tela
    • Fazer capturas de tela

    E o mais importante para as ferramentas de teste de interface: as APIs UiAutomation funcionam além dos limites do aplicativo, ao contrário das APIs em Instrumentation.

    Eventos do Systrace para apps

    O Android 4.3 adiciona a classe Trace com dois métodos estáticos, beginSection() e endSection(), que permitem definir blocos de código para incluir no relatório do Systrace. Ao criar seções de código rastreável no app, os registros do Systrace fornecem uma análise muito mais detalhada de onde ocorre a lentidão no app.

    Para saber como usar a ferramenta Systrace, leia Como analisar a exibição e o desempenho com o Systrace.

    Segurança

    Keystore do Android para chaves particulares de apps

    O Android agora oferece um provedor de segurança Java personalizado no recurso KeyStore, chamado Android Key Store, que permite gerar e salvar chaves privadas que podem ser vistas e usadas apenas pelo seu app. Para carregar o Android Key Store, transmita "AndroidKeyStore" para KeyStore.getInstance().

    Para gerenciar as credenciais particulares do app no Android KeyStore, gere uma nova chave com KeyPairGenerator usando KeyPairGeneratorSpec. Primeiro, receba uma instância de KeyPairGenerator chamando getInstance(). Em seguida, chame initialize(), transmitindo uma instância de KeyPairGeneratorSpec, que pode ser acessada usando KeyPairGeneratorSpec.Builder. Por fim, chame generateKeyPair() para receber o KeyPair.

    Armazenamento de credenciais de hardware

    O Android agora também oferece suporte ao armazenamento de credenciais do KeyChain com suporte de hardware, o que aumenta a segurança ao impedir a extração das chaves. Ou seja, quando as chaves estão em um keystore protegido por hardware (Elemento de segurança, TPM ou TrustZone), elas podem ser usadas para operações criptográficas, mas o material da chave privada não pode ser exportado. Nem mesmo o kernel do SO pode acessar esse material de chave. Nem todos os dispositivos Android oferecem suporte ao armazenamento em hardware, mas é possível verificar no momento da execução se o armazenamento com suporte de hardware está disponível chamando KeyChain.IsBoundKeyAlgorithm().

    Declarações do manifesto

    Recursos obrigatórios declaráveis

    Os valores a seguir agora são compatíveis com o elemento <uses-feature>, para que você possa garantir que o app seja instalado apenas em dispositivos que oferecem os recursos necessários.

    FEATURE_APP_WIDGETS
    Declara que o app fornece um widget e precisa ser instalado apenas em dispositivos que incluem uma tela inicial ou local parecido em que os usuários podem incorporar widgets. Exemplo:
    <uses-feature android:name="android.software.app_widgets" android:required="true" />
    FEATURE_HOME_SCREEN
    Declara que o app se comporta como um substituto da tela inicial e precisa ser instalado apenas em dispositivos que oferecem suporte a apps de tela inicial de terceiros. Exemplo:
    <uses-feature android:name="android.software.home_screen" android:required="true" />
    FEATURE_INPUT_METHODS
    Declara que o app oferece um método de entrada personalizado (um teclado criado com InputMethodService) e precisa ser instalado apenas em dispositivos que oferecem suporte a métodos de entrada de terceiros. Exemplo:
    <uses-feature android:name="android.software.input_methods" android:required="true" />
    FEATURE_BLUETOOTH_LE
    Declara que o app usa APIs Bluetooth de baixa energia e precisa ser instalado apenas em dispositivos que podem se comunicar com outros dispositivos usando essa tecnologia. Exemplo:
    <uses-feature android:name="android.software.bluetooth_le" android:required="true" />

    Permissões do usuário

    Os valores a seguir agora são aceitos no <uses-permission> para declarar as permissões que seu app precisa para acessar determinadas APIs.

    BIND_NOTIFICATION_LISTENER_SERVICE
    É necessário para usar as novas APIs NotificationListenerService.
    SEND_RESPOND_VIA_MESSAGE
    Obrigatório para receber a intent ACTION_RESPOND_VIA_MESSAGE.

    Para uma visualização detalhada de todas as mudanças na API no Android 4.3, consulte o Relatório de diferenças da API.