Nível da API: 19
O Android 4.4 (KITKAT
) é uma nova versão da plataforma Android que oferece novos recursos para usuários e desenvolvedores de apps. Este documento fornece uma introdução às principais novas APIs.
Como desenvolvedor de apps, faça o download da imagem do sistema Android 4.4 e da plataforma do SDK no SDK Manager o mais rápido possível. Se você não tiver um dispositivo com o Android 4.4 para testar o app, use a imagem do sistema Android 4.4 para testar o app no Android Emulator. Em seguida, crie seus apps para a plataforma Android 4.4 para começar a usar as APIs mais recentes.
Atualização do nível da API
Para otimizar melhor o app para dispositivos com Android 4.4,
defina targetSdkVersion
como
"19"
, instale-o em uma imagem do sistema Android 4.4,
teste-o e publique uma atualização com essa mudança.
É possível usar APIs no Android 4.4 e 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 sem suporte ao minSdkVersion
.
Para saber mais sobre como manter a compatibilidade com versões anteriores, leia Compatibilidade com diferentes
versões de plataformas.
Para mais informações sobre como os níveis de API funcionam, leia O que é o nível da API?
Mudanças de comportamento importantes
Se você já publicou um app para Android, ele pode ser afetado pelas mudanças no Android 4.4.
Se seu aplicativo lê dados em armazenamento externo...
O app não pode ler arquivos compartilhados no armazenamento externo ao ser executado no Android 4.4, a menos que ele tenha a permissão READ_EXTERNAL_STORAGE
. Ou seja, os arquivos no diretório retornado por getExternalStoragePublicDirectory()
não podem mais ser acessados sem a permissão. No entanto, se você precisar acessar apenas os diretórios específicos do app, fornecidos por getExternalFilesDir()
, não vai precisar da permissão READ_EXTERNAL_STORAGE
.
Se seu aplicativo usa o WebView...
O app pode se comportar de maneira diferente ao ser executado no Android 4.4, especialmente quando você atualiza o targetSdkVersion
do app para "19" ou mais recente.
O código subjacente à classe WebView
e às APIs relacionadas foi atualizado para se basear em um snapshot moderno do código-fonte do Chromium. Isso traz uma variedade de melhorias de desempenho, suporte a novos recursos do HTML5 e suporte à depuração remota do conteúdo WebView
. O escopo desse upgrade significa que, se o app usar WebView
, o comportamento dele poderá ser afetado em alguns casos. Embora as mudanças de comportamento conhecidas sejam documentadas e afetem principalmente seu app apenas quando você atualiza a targetSdkVersion
para "19" ou mais recente, o novo WebView
opera no "modo quirks" para fornecer algumas funcionalidades legadas em apps direcionados ao nível 18 da API e versões anteriores. É possível que seu app dependa de comportamentos desconhecidos da versão anterior de WebView
.
Portanto, se o app já usa WebView
, é importante testar no Android 4.4 assim que possível e consultar a página Como migrar para a WebView no Android 4.4 para saber como o app pode ser afetado quando você atualiza o targetSdkVersion
para "19" ou mais recente.
Se seu aplicativo usa o AlarmManager...
Quando você define a targetSdkVersion
do app como "19" ou mais recente, os alarmes criados usando set()
ou setRepeating()
são imprecisos.
Para aumentar a eficiência do consumo de energia, agora o Android agrupa alarmes que ocorrem em momentos razoavelmente similares de todos os aplicativos para que o sistema "desperte" o dispositivo uma vez só, em vez de diversas vezes para tratar de cada alarme.
Se o alarme não estiver associado a um horário exato, mas ainda for importante que ele seja acionado durante um período específico (por exemplo, entre 14h e 16h), use o novo método setWindow()
, que aceita um horário "mais cedo" para o alarme e uma "janela" de tempo após o horário mais cedo em que o sistema precisa acionar o alarme.
Se o alarme precisar ser fixado em um horário exato (por exemplo, para um lembrete de evento do Google Agenda), use o novo método setExact()
.
Esse comportamento de agrupamento inexato aplica-se somente a aplicativos atualizados. Se você definiu o targetSdkVersion
como "18" ou menos, os alarmes vão continuar se comportando como nas versões anteriores ao serem executados no Android 4.4.
Se seu aplicativo sincroniza dados usando o ContentResolver...
Ao definir a targetSdkVersion
do app como "19" ou mais recente, a criação de uma sincronização com addPeriodicSync()
vai realizar as operações de sincronização em um intervalo flexível padrão de aproximadamente 4% do período especificado. Por exemplo, se a frequência da sua enquete for 24 horas, a operação de sincronização pode ocorrer em uma janela de tempo de cerca de uma hora todos os dias, em vez de exatamente na mesma hora.
Para especificar seu próprio intervalo flexível para operações de sincronização, comece a usar o novo método requestSync()
. Para mais detalhes, consulte a seção abaixo sobre Adaptadores de sincronização.
Esse comportamento de intervalo flexível aplica-se somente a aplicativos atualizados. Se você definiu a targetSdkVersion
como "18" ou menor, suas solicitações de sincronização atuais vão continuar se comportando como nas versões anteriores ao serem executadas no Android 4.4.
Estrutura de impressão
Agora o Android contém uma estrutura completa que permite que os usuários imprimam qualquer documento usando uma impressora conectada por Wi-Fi, Bluetooth ou outros serviços. O sistema lida com a operação entre um aplicativo que deseja imprimir um documento e os serviços que fornecem trabalhos de impressão a uma impressora. O framework android.print
fornece todas as APIs necessárias para especificar um documento de impressão e enviá-lo ao sistema para impressão. De quais APIs você realmente precisa para determinado trabalho de impressão depende do conteúdo.
Impressão de conteúdo genérico
Se você quiser imprimir o conteúdo da interface como um documento, primeiro crie uma subclasse de PrintDocumentAdapter
. Nessa classe, é necessário implementar alguns métodos de callback, incluindo onLayout()
para estabelecer o layout com base nas propriedades de impressão fornecidas e onWrite()
para serializar o conteúdo imprimível em um ParcelFileDescriptor
.
Para gravar o conteúdo na ParcelFileDescriptor
, você precisa transmitir um PDF. As novas APIs PdfDocument
oferecem uma maneira conveniente de fazer isso, fornecendo um Canvas
de getCanvas()
, em que você pode desenhar seu conteúdo para impressão. Em seguida, grave o PdfDocument
no ParcelFileDescriptor
usando o método writeTo()
.
Depois de definir a implementação de PrintDocumentAdapter
, você pode executar trabalhos de impressão conforme a solicitação do usuário usando o método PrintManager
, print()
, que usa o PrintDocumentAdapter
como um dos argumentos.
Impressão de imagens
Se você quiser imprimir apenas uma foto ou outro bitmap, as APIs de assistente da biblioteca de suporte fazem todo o trabalho para você. Basta criar uma nova instância de PrintHelper
, definir o modo de escalonamento com setScaleMode()
e transmitir o Bitmap
para printBitmap()
. É isso. A biblioteca lida com todas as interações restantes com o sistema para fornecer o bitmap à impressora.
Criação de serviços de impressão
Como OEM de impressoras, você pode usar o framework android.printservice
para oferecer interoperabilidade com suas impressoras em dispositivos Android. É possível criar e distribuir serviços de impressão como APKs, que os usuários podem instalar nos dispositivos. Um app de serviço de impressão opera principalmente como um serviço sem cabeça, subclassificando a classe PrintService
, que recebe trabalhos de impressão do sistema e os comunica às impressoras usando os protocolos apropriados.
Para saber mais sobre como imprimir o conteúdo do app, leia Imprimir conteúdo.
Provedor de SMS
O provedor de conteúdo Telephony
(o "provedor de SMS") permite que os apps leiam e gravem mensagens SMS e MMS no dispositivo. Incluem-se tabelas de mensagens de SMS e MMS recebidas, de rascunho, enviadas, pendentes, entre outras.
A partir do Android 4.4, as configurações do sistema permitem que os usuários selecionem um "app de SMS padrão". Depois de selecionado, apenas o app de SMS padrão pode gravar no provedor de SMS, e apenas o app de SMS padrão recebe a transmissão SMS_DELIVER_ACTION
quando o usuário recebe um SMS ou a transmissão WAP_PUSH_DELIVER_ACTION
quando o usuário recebe um MMS. O aplicativo de SMS padrão é responsável por gravar detalhes no Provedor de SMS quando ele recebe ou envia uma nova mensagem.
Outros apps que não são selecionados como o app de SMS padrão só podem ler o provedor de SMS, mas também podem ser notificados quando um novo SMS chega ao detectar a transmissão SMS_RECEIVED_ACTION
, que é uma transmissão não abortável que pode ser enviada para vários apps. Essa transmissão visa os aplicativos que, embora não selecionados como o aplicativo de SMS padrão, precisam ler mensagens recebidas especiais, como para realizar verificação do número do telefone.
Para mais informações, leia a postagem do blog Como preparar seus apps de SMS para o KitKat.
Redes sem fio e conectividade
Emulação de cartão host
Os aplicativos Android agora podem emular cartões NFC ISO14443-4 (ISO-DEP) que usam APDUs para troca de dados (conforme especificado em ISO7816-4). Isso permite que um dispositivo com NFC integrado e Android 4.4 emule diversos cartões NFC ao mesmo tempo, além de permitir que um terminal de pagamento NFC ou outro leitor NFC inicie uma transação com o cartão NFC em questão com base no identificador do aplicativo (AID).
Se você quiser emular um cartão NFC que usa esses protocolos no seu app, crie um componente de serviço com base na classe HostApduService
. Se o app usar um elemento de segurança para a emulação de cartão, será necessário criar um serviço com base na classe OffHostApduService
, que não estará diretamente envolvido nas transações, mas é necessário para registrar os AIDs que precisam ser processados pelo elemento de segurança.
Para mais informações, leia o guia Emulação de cartão NFC.
Modo de leitor NFC
Um novo modo de leitor NFC permite que uma atividade restrinja todas as atividades de NFC para somente ler os tipos de tag em que a atividade está interessada quando está em primeiro plano. É possível ativar o modo leitor para sua atividade com enableReaderMode()
, fornecendo uma implementação de NfcAdapter.ReaderCallback
que recebe um callback quando novas tags são detectadas.
Esse novo recurso, em conjunto com a emulação de cartão do host, permite que o Android opere em ambas as extremidades de uma interface de pagamento móvel: um dispositivo opera como o terminal de pagamento (um dispositivo que executa uma atividade no modo de leitor) e outro dispositivo opera como o cliente de pagamento (um dispositivo que emula um cartão NFC).
Transmissores infravermelhos
Ao executar em um dispositivo que inclui um transmissor de infravermelho (IR), agora é possível transmitir sinais de infravermelho usando as APIs ConsumerIrManager
. Para receber uma instância de ConsumerIrManager
, chame getSystemService()
com CONSUMER_IR_SERVICE
como argumento. Em seguida, é possível consultar as frequências de infravermelho compatíveis do dispositivo com getCarrierFrequencies()
e transmitir sinais transmitindo a frequência e o padrão de sinal desejados com transmit()
.
Sempre verifique se um dispositivo inclui um transmissor de infravermelho chamando hasIrEmitter()
. No entanto, se o app for compatível apenas com dispositivos que têm um transmissor, inclua um elemento <uses-feature>
no manifesto para "android.hardware.consumerir"
(FEATURE_CONSUMER_IR
).
Multimídia
Reprodução adaptativa
O suporte à reprodução de vídeo adaptativa agora está disponível com as APIs MediaCodec
, permitindo a mudança contínua na resolução durante a reprodução em um Surface
. É possível alimentar os frames de entrada do decodificador de uma nova resolução, e a resolução dos buffers de saída muda sem uma lacuna significativa.
É possível ativar a reprodução adaptativa adicionando duas chaves a MediaFormat
que especificam a resolução máxima que o app exige do codec: KEY_MAX_WIDTH
e KEY_MAX_HEIGHT
. Com esses recursos adicionados ao MediaFormat
, transmita o MediaFormat
para a instância MediaCodec
com configure()
.
O codec fara uma transição entre resoluções que tenham valores iguais ou inferiores a esses especificados de forma muito suave. O codec pode ainda funcionar com resoluções com valores superiores aos máximos especificados (desde que estejam dentro dos limites dos perfis compatíveis), mas as transições para resoluções maiores pode não ser suave.
Para alterar a resolução durante a decodificação de vídeo H.264, continue enfileirando quadros usandoMediaCodec.queueInputBuffer(), mas não deixe de fornecer os novos valores de Conjunto de parâmetros de sequência (SPS) e Conjunto de parâmetros de imagem (PPS) juntos com o quadro Atualização instantânea do decodificador (IDR) em um único buffer.
No entanto, antes de tentar configurar o codec para reprodução adaptativa, chame isFeatureSupported(String)
com FEATURE_AdaptivePlayback
para verificar se o dispositivo oferece suporte a esse tipo de reprodução.
Observação:o suporte à reprodução adaptativa é específico do fornecedor. Alguns codecs podem exigir mais memória para maiores sugestões de resolução. Assim, você deve definir os máximos da resolução com base no material de origem que estiver decodificando.
Marcações de data e hora de áudio sob demanda
Para facilitar a sincronização de áudio e vídeo, a nova classe AudioTimestamp
fornece detalhes da linha do tempo sobre um "frame" específico em um stream de áudio processado por AudioTrack
. Para receber o carimbo de data/hora mais recente disponível, instancie um objeto AudioTimestamp
e transmita-o para getTimestamp()
. Se a solicitação do carimbo de data/hora for bem-sucedida, a instância AudioTrack
será preenchida com uma posição em unidades de frame, junto com o tempo estimado em que esse frame foi apresentado ou será apresentado.
Você pode usar o valor de nanoTime
no AudioTimestamp
(que é monotonicamente) para encontrar o frame de vídeo associado mais próximo em comparação com framePosition
. Assim, você pode soltar, duplicar ou interpolar frames de vídeo para que eles correspondam ao áudio. Como alternativa, você pode determinar o tempo delta entre o valor de nanoTime
e o tempo esperado de um frame de vídeo futuro (considerando a taxa de amostragem) para prever qual frame de áudio é esperado no mesmo momento que um frame de vídeo.
Leitor de imagens de superfície
A nova API ImageReader
oferece acesso direto aos buffers de imagem conforme eles são renderizados em um Surface
. É possível adquirir um ImageReader
com o método estático newInstance()
. Em seguida, chame getSurface()
para criar um novo Surface
e entregar os dados da imagem com um produtor, como MediaPlayer
ou MediaCodec
. Para receber notificações quando novas imagens estiverem disponíveis na plataforma, implemente a interface ImageReader.OnImageAvailableListener
e registre-a com setOnImageAvailableListener()
.
Agora, ao desenhar conteúdo no Surface
, o ImageReader.OnImageAvailableListener
recebe uma chamada para onImageAvailable()
quando cada novo frame de imagem fica disponível, fornecendo o ImageReader
correspondente. É possível usar o ImageReader
para adquirir os dados de imagem do frame como um objeto Image
chamando acquireLatestImage()
ou acquireNextImage()
.
O objeto Image
fornece acesso direto ao carimbo de data/hora, formato, dimensões e dados de pixel da imagem em um ByteBuffer
. No entanto, para que a classe Image
interprete suas imagens, elas precisam ser formatadas de acordo com um dos tipos definidos por constantes em ImageFormat
ou PixelFormat
.
Medição de pico e RMS
Agora é possível consultar o pico e o RMS do stream de áudio atual de Visualizer
criando uma nova instância de Visualizer.MeasurementPeakRms
e transmitindo-a para getMeasurementPeakRms()
. Quando você chama esse método, os valores de pico e RMS do Visualizer.MeasurementPeakRms
especificado são definidos como os valores medidos mais recentes.
Atenuador de ruído
O LoudnessEnhancer
é uma nova subclasse de AudioEffect
que permite aumentar o volume audível do MediaPlayer
ou do AudioTrack
. Isso pode ser especialmente útil em conjunto com o novo método getMeasurementPeakRms()
mencionado acima, para aumentar o volume das faixas de áudio faladas enquanto outras mídias estão sendo tocadas.
Controladores remotos
O Android 4.0 (nível 14 da API) introduziu as APIs RemoteControlClient
, que permitem que apps de mídia consumam eventos do media controller de clientes remotos, como controles de mídia na tela de bloqueio. Agora, as novas APIs RemoteController
permitem criar seu próprio controle remoto, possibilitando a criação de novos apps e periféricos inovadores que podem controlar a reprodução de qualquer app de mídia integrado ao RemoteControlClient
.
Para criar um controle remoto, você pode implementar a interface do usuário da maneira que quiser, mas para enviar os eventos do botão de mídia ao app de mídia do usuário, é necessário criar um serviço que estenda a classe NotificationListenerService
e implemente a interface RemoteController.OnClientUpdateListener
. O uso de NotificationListenerService
como base é importante porque oferece as restrições de privacidade adequadas, que exigem que os usuários ativem o app como um listener de notificações nas configurações de segurança do sistema.
A classe NotificationListenerService
inclui alguns métodos abstratos que você precisa implementar, mas se você só se preocupa com os eventos do controlador de mídia para processar a reprodução de mídia, deixe a implementação vazia e se concentre nos métodos RemoteController.OnClientUpdateListener
.
Classificação de controladores remotos
O Android 4.4 se baseia nos recursos existentes para clientes de controle remoto (apps que recebem eventos de controle de mídia com o RemoteControlClient
) e adiciona a capacidade de os usuários classificarem a faixa atual no controle remoto.
A nova classe Rating
encapsula informações sobre a classificação de um usuário. Uma classificação é definida pelo estilo de classificação (RATING_HEART
, RATING_THUMB_UP_DOWN
, RATING_3_STARS
, RATING_4_STARS
, RATING_5_STARS
ou RATING_PERCENTAGE
) e pelo valor de classificação apropriado para esse estilo.
Para que os usuários possam classificar suas trilhas por um controlador remoto:
- Sinalize que você quer expor a IU de classificação ao usuário (se aplicável) adicionando a flag
FLAG_KEY_MEDIA_RATING
emsetTransportControlFlags()
. - Chame
editMetadata()
para recuperar umRemoteControlClient.MetadataEditor
e transmita-oRATING_KEY_BY_USER
comaddEditableKey()
. - Em seguida, especifique o estilo de classificação chamando
putObject()
e transmitindoRATING_KEY_BY_USER
como chave e um dos estilos de classificação acima como valor.
Para receber um callback quando o usuário mudar a classificação no controle remoto, implemente a nova interface RemoteControlClient.OnMetadataUpdateListener
e transmita uma instância para setMetadataUpdateListener()
. Quando o usuário muda a classificação, o RemoteControlClient.OnMetadataUpdateListener
recebe uma chamada para onMetadataUpdate()
, transmitindo RATING_KEY_BY_USER
como a chave e um objeto Rating
como o valor.
Legendas ocultas
Agora, o VideoView
oferece suporte a faixas de legenda WebVTT ao reproduzir vídeos HTTP Live Stream (HLS), mostrando a faixa de acordo com as preferências de legenda que o usuário definiu nas configurações do sistema.
Também é possível fornecer VideoView
com suas faixas de legenda do WebVTT usando o método addSubtitleSource()
. Esse método aceita um InputStream
que carrega os dados de legenda e um objeto MediaFormat
que especifica o formato dos dados de legenda, que pode ser especificado usando createSubtitleFormat()
. Essas legendas também aparecem sobre o vídeo de acordo com as preferências do usuário.
Se você não usar VideoView
para exibir o conteúdo do vídeo, faça com que a sobreposição de legendas corresponda o máximo possível à preferência de legendagem descritiva do usuário. Uma nova API CaptioningManager
permite consultar as preferências de legenda descritiva do usuário, incluindo estilos definidos por CaptioningManager.CaptionStyle
, como família tipográfica e cor. Caso o usuário ajuste algumas preferências depois que o vídeo já tiver começado, ouça as mudanças nas preferências registrando uma instância de CaptioningManager.CaptioningChangeListener
para receber um callback quando alguma das preferências mudar e atualize as legendas conforme necessário.
Animação e gráficos
Cenas e transições
O novo framework android.transition
oferece APIs que facilitam animações entre diferentes estados da interface do usuário. Um recurso importante é a capacidade de definir estados distintos da interface, conhecidos como "cenas", criando um layout separado para cada um. Quando você quer animar de uma cena para outra, execute uma "transição", que calcula a animação necessária para mudar o layout da cena atual para a próxima.
Para realizar a transição entre duas cenas, você geralmente precisa realizar o seguinte:
- Especifique o
ViewGroup
que contém os componentes da interface que você quer mudar. - Especifique o layout que representa o resultado final da mudança (a próxima cena).
- Especifique o tipo de transição que deve animar a mudança de layout.
- Execute a transição.
Você pode usar um objeto Scene
para realizar as etapas 1 e 2. Um Scene
contém metadados que descrevem as propriedades de um layout necessárias para realizar uma transição, incluindo a visualização pai da cena e o layout da cena. É possível criar um Scene
usando um construtor de classe ou o método estático getSceneForLayout()
.
Em seguida, use o TransitionManager
para realizar as etapas 3 e 4. Uma maneira é transmitir o Scene
para o método estático go()
. Isso encontra a visualização mãe da cena no layout atual e realiza uma transição nas visualizações filhas para alcançar o layout definido pelo Scene
.
Como alternativa, não é necessário criar um objeto Scene
, mas é possível chamar beginDelayedTransition()
, especificando um ViewGroup
que contém as visualizações que você quer mudar. Depois adicione, remova ou reconfigure as visualizações que quiser. Depois que o sistema aplicar as mudanças conforme o necessário, uma transição começará a animar todas as visualizações afetadas.
Para ter mais controle, você pode definir conjuntos de transições que devem ocorrer entre cenas predefinidas usando um arquivo XML no diretório res/transition/
do projeto. Dentro de um elemento <transitionManager>
, especifique uma ou mais tags <transition>
que especificam uma cena (uma referência a um arquivo de layout) e a transição a ser aplicada ao entrar e/ou sair dessa cena. Em seguida, infle esse conjunto de transições usando inflateTransitionManager()
. Use o TransitionManager
retornado para executar cada transição com transitionTo()
, transmitindo um Scene
representado por uma das tags <transition>
. Também é possível definir conjuntos de transições de maneira programática com as APIs TransitionManager
.
Ao especificar uma transição, você pode usar vários tipos predefinidos definidos por subclasses de Transition
, como Fade
e ChangeBounds
. Se você não especificar um tipo de transição, o sistema usará AutoTransition
por padrão, que desbota, move e redimensiona as visualizações automaticamente conforme necessário. Além disso, é possível criar transições personalizadas estendendo qualquer uma dessas classes para executar as animações da maneira que quiser. Uma transição personalizada pode acompanhar qualquer mudança de propriedade que você quiser e criar qualquer animação com base nessas mudanças. Por exemplo, é possível fornecer uma subclasse de Transition
que detecta mudanças na propriedade "rotação" de uma visualização e as anima.
Para mais informações, consulte a documentação TransitionManager
.
Pausa do animador
As APIs Animator
agora permitem pausar e retomar uma animação em andamento com os métodos pause()
e resume()
.
Para acompanhar o estado de uma animação, implemente a interface Animator.AnimatorPauseListener
, que fornece callbacks quando uma animação é pausada e retomada: pause()
e resume()
. Em seguida, adicione o listener a um objeto Animator
com addPauseListener()
.
Como alternativa, é possível criar uma subclasse da classe abstrata AnimatorListenerAdapter
, que agora inclui implementações vazias para os callbacks de pausa e retomada definidos por Animator.AnimatorPauseListener
.
Bitmaps reutilizáveis
Agora é possível reutilizar qualquer bitmap mutável em BitmapFactory
para decodificar qualquer outro bitmap, mesmo que o novo bitmap tenha um tamanho diferente, desde que a contagem de bytes resultante do bitmap decodificado (disponível em getByteCount()
) seja menor ou igual à contagem de bytes alocada do bitmap reutilizado (disponível em getAllocationByteCount()
). Para mais informações, consulte inBitmap
.
As novas APIs para Bitmap
permitem uma reconfiguração semelhante para reutilização fora de BitmapFactory
(para geração manual de bitmap ou lógica de decodificação personalizada). Agora é possível definir as dimensões de um bitmap com os métodos setHeight()
e setWidth()
e especificar um novo Bitmap.Config
com setConfig()
sem afetar a alocação de bitmap subjacente. O método reconfigure()
também oferece uma maneira conveniente de combinar essas mudanças com uma chamada.
No entanto, não reconfigure um bitmap que está sendo usado pelo sistema de visualização, porque o buffer de pixel subjacente não será remapeado de maneira previsível.
Conteúdo do usuário
Estrutura de acesso ao armazenamento
Em versões anteriores do Android, para o app recuperar um tipo específico de arquivo de outro app, ele precisa invocar uma intent com a ação ACTION_GET_CONTENT
. Essa ação ainda é a maneira adequada de solicitar um arquivo que você quer importar para o app. No entanto, o Android 4.4 apresenta a ação ACTION_OPEN_DOCUMENT
, que permite que o usuário selecione um arquivo de um tipo específico e conceda ao app acesso de leitura de longo prazo a esse arquivo (possivelmente com acesso de gravação) sem importar o arquivo para o app.
Se você está desenvolvendo um app que oferece serviços de armazenamento de arquivos, como o recurso "Salvar na nuvem", é possível participar dessa interface unificada para escolher arquivos implementando um provedor de conteúdo como uma subclasse da nova classe DocumentsProvider
. Sua subclasse de DocumentsProvider
precisa incluir um filtro de intent que aceite a ação PROVIDER_INTERFACE
("android.content.action.DOCUMENTS_PROVIDER"
). Em seguida, implemente os quatro métodos abstratos no DocumentsProvider
:
queryRoots()
- Isso precisa retornar um
Cursor
que descreva todos os diretórios raiz do armazenamento de documentos usando as colunas definidas emDocumentsContract.Root
. queryChildDocuments()
- Ela precisa retornar um
Cursor
que descreva todos os arquivos no diretório especificado usando as colunas definidas emDocumentsContract.Document
. queryDocument()
- Ela precisa retornar um
Cursor
que descreva o arquivo especificado usando as colunas definidas emDocumentsContract.Document
. openDocument()
- Ela precisa retornar um
ParcelFileDescriptor
que represente o arquivo especificado. O sistema chama esse método quando o usuário seleciona um arquivo e o app cliente solicita acesso chamandoopenFileDescriptor()
.
Para mais informações, consulte o guia Framework de acesso ao armazenamento.
Acesso a armazenamento externo
Agora é possível ler e gravar arquivos específicos do aplicativo em dispositivos de armazenamento externo secundário, como quando um dispositivo fornece armazenamento emulado e um cartão SD. O novo método getExternalFilesDirs()
funciona da mesma forma que o método getExternalFilesDir()
, exceto por retornar uma matriz de objetos File
. Antes de ler ou gravar em qualquer um dos caminhos retornados por esse método, transmita o objeto File
para o novo método getStorageState()
para verificar se o armazenamento está disponível.
Outros métodos de acesso ao diretório de cache específico do app e ao diretório OBB também têm versões correspondentes que fornecem acesso a dispositivos de armazenamento secundário: getExternalCacheDirs()
e getObbDirs()
, respectivamente.
A primeira entrada na matriz File
retornada é considerada o armazenamento externo principal do dispositivo, que é o mesmo que o File
retornado por métodos existentes, como getExternalFilesDir()
.
Observação:a partir do Android 4.4, a plataforma não exige mais que o app adquira o WRITE_EXTERNAL_STORAGE
ou o READ_EXTERNAL_STORAGE
quando você precisa acessar apenas as regiões específicas do app no armazenamento externo usando os métodos acima. No entanto, as permissões são necessárias se você quiser acessar as regiões compartilháveis do armazenamento externo, fornecidas por getExternalStoragePublicDirectory()
.
Adaptadores de sincronização
O novo método requestSync()
em ContentResolver
simplifica parte do procedimento para definir uma solicitação de sincronização para o ContentProvider
, encapsulando solicitações no novo objeto SyncRequest
, que pode ser criado com SyncRequest.Builder
. As propriedades em SyncRequest
oferecem a mesma funcionalidade das chamadas de sincronização ContentProvider
atuais, mas adicionam a capacidade de especificar que uma sincronização precisa ser descartada se a rede for medida, ativando setDisallowMetered()
.
Entrada do usuário
Novos tipos de sensor
O novo sensor TYPE_GEOMAGNETIC_ROTATION_VECTOR
fornece dados de vetor de rotação com base em um magnetômetro, que é uma alternativa útil ao sensor TYPE_ROTATION_VECTOR
quando um giroscópio não está disponível ou quando usado com eventos de sensor em lote para registrar a orientação do dispositivo enquanto o smartphone está em repouso. Esse sensor requer menos energia do que o TYPE_ROTATION_VECTOR
, mas pode ser propenso a dados de eventos com ruídos e é mais eficaz quando o usuário está ao ar livre.
Além disso, o Android agora tem compatibilidade com sensores de passo integrados no hardware:
TYPE_STEP_DETECTOR
- Esse sensor aciona um evento sempre que o usuário dá um passo. Em cada etapa do usuário, esse sensor gera um evento com um valor de 1,0 e um carimbo de data/hora indicando quando a etapa ocorreu.
TYPE_STEP_COUNTER
- Esse sensor também aciona um evento a cada passo detectado, mas fornece o número total de passos acumulados desde que o sensor foi registrado pela primeira vez por um app.
Esses dois sensores de passos não sempre apresentam os mesmos resultados. Os eventos TYPE_STEP_COUNTER
ocorrem com uma latência maior do que os de TYPE_STEP_DETECTOR
, mas isso ocorre porque o algoritmo TYPE_STEP_COUNTER
faz mais processamento para eliminar falsos positivos. Portanto, o TYPE_STEP_COUNTER
pode ser mais lento para enviar eventos, mas os resultados são mais precisos.
Os dois sensores de passo dependem do hardware (o Nexus 5 é o primeiro dispositivo compatível com eles). Portanto, verifique a disponibilidade com hasSystemFeature()
, usando as constantes FEATURE_SENSOR_STEP_DETECTOR
e FEATURE_SENSOR_STEP_COUNTER
.
Eventos de sensor agrupados
Para gerenciar melhor a energia do dispositivo, as APIs SensorManager
agora permitem especificar a frequência com que você quer que o sistema envie lotes de eventos de sensor para o app. Isso não reduz o número de eventos de sensor reais disponíveis para o app em um determinado período, mas reduz a frequência com que o sistema chama o SensorEventListener
com atualizações do sensor. Ou seja, em vez de enviar cada evento ao aplicativo assim que ocorre, o sistema salva todos os eventos que ocorrem durante um intervalo de tempo e entrega todos juntos.
Para fornecer o lote, a classe SensorManager
adiciona duas novas versões do método registerListener()
que permitem especificar a "latência máxima do relatório". Esse novo parâmetro especifica o atraso máximo que o SensorEventListener
tolera para a entrega de novos eventos do sensor. Por exemplo, se você especificar uma latência de lote de um minuto, o sistema vai enviar o conjunto recente de eventos em lote em um intervalo de até um minuto fazendo chamadas consecutivas para o método onSensorChanged()
, uma para cada evento que foi agrupado. Os eventos de sensor nunca excederão o valor da latência máxima de comunicação, mas podem chegar antes se outros aplicativos solicitarem uma latência menor para o mesmo sensor.
No entanto, o sensor vai enviar os eventos agrupados ao app com base na latência do relatório somente enquanto a CPU estiver ativa. Embora o sensor de um equipamento compatível com agrupamento continue coletando eventos de sensor com o CPU suspenso, ele não ativará o CPU para enviar os eventos agrupados ao seu aplicativo. Quando, por acaso, o sensor ficar sem memória para eventos, ele começará a remover os eventos mais antigos para salvar os eventos mais novos. Para evitar a perda de eventos, ative o dispositivo antes que o sensor preencha a memória e chame flush()
para capturar o lote mais recente de eventos. Para estimar quando a memória vai estar cheia e precisa ser limpa, chame getFifoMaxEventCount()
para receber o número máximo de eventos do sensor que ela pode salvar e divida esse número pela taxa em que o app quer cada evento. Use esse cálculo para definir alarmes de ativação com AlarmManager
que invocam o Service
(que implementa o SensorEventListener
) para limpar o sensor.
Observação:nem todos os dispositivos oferecem suporte a eventos de sensor em lote, porque isso exige suporte do sensor de hardware. No entanto, a partir do Android 4.4, sempre use os novos métodos registerListener()
. Se o dispositivo não oferecer suporte a lotes, o sistema vai ignorar o argumento de latência de lote e enviar eventos do sensor em tempo real.
Identidade dos controladores
O Android agora identifica cada controlador conectado com um número inteiro exclusivo que pode ser consultado com getControllerNumber()
, facilitando a associação de cada controlador a um jogador diferente em um jogo. O número de cada controlador pode mudar devido à desconexão, conexão ou reconfiguração dos controladores pelo usuário. Portanto, é necessário acompanhar qual número de controlador corresponde a cada dispositivo de entrada registrando uma instância de InputManager.InputDeviceListener
. Em seguida, chame getControllerNumber()
para cada InputDevice
quando uma mudança ocorrer.
Os dispositivos conectados também fornecem IDs de produto e de fornecedor disponíveis em getProductId()
e getVendorId()
. Se você precisar modificar os mapeamentos de chaves com base no conjunto de chaves disponível em um dispositivo, consulte o dispositivo para verificar se determinadas chaves estão disponíveis com hasKeys(int...)
.
Interface do usuário
Modo de tela cheia imersivo
Para fornecer ao app um layout que preencha toda a tela, a nova flag SYSTEM_UI_FLAG_IMMERSIVE
para setSystemUiVisibility()
(quando combinada com SYSTEM_UI_FLAG_HIDE_NAVIGATION
) ativa um novo modo de tela cheia imersivo. Mesmo com o modo de tela cheia imersivo ativado, sua atividade continua recebendo todos os eventos de toque. O usuário pode revelar as barras do sistema com um movimento para dentro junto da região em que as barras do sistema normalmente aparecem. Isso limpa a flag SYSTEM_UI_FLAG_HIDE_NAVIGATION
(e a flag SYSTEM_UI_FLAG_FULLSCREEN
, se aplicada) para que as barras do sistema permaneçam visíveis. No entanto, se você quiser que as barras do sistema sejam ocultadas novamente após alguns instantes, use a flag SYSTEM_UI_FLAG_IMMERSIVE_STICKY
.
Barras do sistema translúcidas
Agora é possível deixar as barras do sistema parcialmente translúcidas com novos temas, Theme.Holo.NoActionBar.TranslucentDecor
e Theme.Holo.Light.NoActionBar.TranslucentDecor
. Ao ativar barras de sistema translúcidas, o layout vai preencher a área atrás das barras de sistema. Portanto, você também precisa ativar fitsSystemWindows
para a parte do layout que não deve ser coberta pelas barras de sistema.
Se você estiver criando um tema personalizado, defina um desses temas como o tema pai ou inclua as propriedades de estilo windowTranslucentNavigation
e windowTranslucentStatus
no seu tema.
Ouvinte de notificação aprimorado
O Android 4.3 adicionou as APIs NotificationListenerService
, permitindo que os apps recebam informações sobre novas notificações conforme elas são postadas pelo sistema. No Android 4.4, os listeners de notificação podem recuperar outros metadados para a notificação e preencher detalhes sobre as ações dela:
O novo campo Notification.extras
inclui um Bundle
para enviar metadados adicionais do criador de notificações, como EXTRA_TITLE
e EXTRA_PICTURE
.
A nova classe Notification.Action
define as características de uma ação anexada à notificação, que pode ser recuperada do novo campo actions
.
Espelhamento de desenháveis para layouts RTL
Em versões anteriores do Android, se o app incluir imagens que precisam reverter a orientação horizontal para layouts da direita para a esquerda, é necessário incluir a imagem espelhada em um diretório de recursos drawables-ldrtl/
. Agora, o sistema pode espelhar imagens automaticamente para você ativando o atributo autoMirrored
em um recurso drawable ou chamando setAutoMirrored()
. Quando ativado, o Drawable
é espelhado automaticamente quando a direção do layout é da direita para a esquerda.
Acessibilidade
A classe View
agora permite declarar "regiões ativas" para partes da interface que são atualizadas dinamicamente com novo conteúdo de texto. Para isso, adicione o novo atributo accessibilityLiveRegion
ao layout XML ou chame setAccessibilityLiveRegion()
. Por exemplo, uma tela de login com um campo de texto que mostra uma notificação de "senha incorreta" precisa ser marcada como uma região ativa para que o leitor de tela recite a mensagem quando ela mudar.
Os apps que oferecem um serviço de acessibilidade agora também podem melhorar os recursos com novas APIs que fornecem informações sobre coleções de visualizações, como visualizações de lista ou grade, usando AccessibilityNodeInfo.CollectionInfo
e AccessibilityNodeInfo.CollectionItemInfo
.
Permissões do app
Confira a seguir as novas permissões que o app precisa solicitar com a tag <uses-permission>
para usar determinadas APIs:
INSTALL_SHORTCUT
- Permite que um app instale um atalho no Acesso rápido
UNINSTALL_SHORTCUT
- Permite que um app desinstale um atalho no Acesso rápido aos apps
TRANSMIT_IR
- Permite que um aplicativo use o transmissor infravermelho do dispositivo, se disponível
Observação:a partir do Android 4.4, a plataforma não exige mais que o app adquira WRITE_EXTERNAL_STORAGE
ou READ_EXTERNAL_STORAGE
quando você quiser acessar as regiões específicas do app no armazenamento externo usando métodos como getExternalFilesDir()
. No entanto, as permissões ainda são necessárias se você quiser acessar as regiões compartilháveis do armazenamento externo, fornecidas por getExternalStoragePublicDirectory()
.
Recursos do dispositivo
Confira a seguir os novos recursos do dispositivo que podem ser declarados com a tag <uses-feature>
para declarar os requisitos do app e ativar a filtragem no Google Play ou verificar no momento da execução:
FEATURE_CONSUMER_IR
- O dispositivo é capaz de se comunicar com dispositivos IR do consumidor.
FEATURE_DEVICE_ADMIN
- O dispositivo oferece suporte à aplicação de políticas por administradores.
FEATURE_NFC_HOST_CARD_EMULATION
- O dispositivo oferece suporte à emulação de cartão NFC com base no host.
FEATURE_SENSOR_STEP_COUNTER
- O dispositivo inclui um contador de passos de hardware.
FEATURE_SENSOR_STEP_DETECTOR
- O dispositivo inclui um detector de passos de hardware.
Para conferir uma visão detalhada de todas as mudanças na API do Android 4.4, consulte o Relatório de diferenças da API.