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_BOOLEANpara uma restrição que é verdadeira ou falsa.TYPE_CHOICEpara uma restrição que tem várias opções mutuamente exclusivas (opções de botão de opção).TYPE_MULTI_SELECTpara 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:
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.
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.
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.