Nivel de API: 19
Android 4.4 (KITKAT
) es un lanzamiento reciente de la plataforma Android que ofrece funciones nuevas a los usuarios y los desarrolladores de apps. En este documento se ofrece una introducción a las API nuevas más distinguidas.
Si eres desarrollador de apps, debes descargar la imagen de sistema Android 4.4 y la plataforma del SDK desde SDK Manager lo más pronto posible. Si no tienes un dispositivo con Android 4.4 en el cual probar tu app, usa la imagen de sistema de Android 4.4 para probarla en Android Emulator. Luego, compila las apps con la plataforma Android 4.4 para comenzar a usar las API más recientes.
Actualización del nivel de tu API de destino
A fin de optimizar mejor tu app para los dispositivos que tienen Android 4.4, debes fijar el atributo targetSdkVersion
en "19"
, instalar la app en la imagen de sistema de Android 4.4, probarla y luego publicar una actualización con este cambio.
Puedes usar API en Android 4.4 y al mismo tiempo mantener la compatibilidad con versiones anteriores agregando a tu código condiciones que comprueben el nivel de API del sistema antes de ejecutar las API no admitidas por el atributo minSdkVersion
. Para obtener más información sobre cómo mantener la compatibilidad con versiones anteriores, consulta Compatibilidad con versiones de plataforma diferentes.
Para obtener más información sobre el funcionamiento de los niveles de API, consulta ¿Qué es un nivel de API?.
Importantes cambios en los comportamientos
Si has publicado anteriormente una app para Android, ten en cuenta que tu app podría verse afectada por los cambios en Android 4.4.
Si tu app realiza operaciones de lectura desde un medio de almacenamiento externo...
La app no puede leer archivos compartidos en el medio de almacenamiento externo cuando se ejecuta en Android 4.4, a menos que tenga el permiso READ_EXTERNAL_STORAGE
. Esto quiere decir que, sin el permiso, ya no es posible tener acceso a los archivos dentro del directorio mostrados por getExternalStoragePublicDirectory()
. Sin embargo, si tienes que acceder solamente a los directorios específicos de tu app, proporcionados por getExternalFilesDir()
, no necesitas el permiso READ_EXTERNAL_STORAGE
.
Si tu app usa WebView...
Tu app podría comportarse de manera diferente al ejecutarse en Android 4.4, en especial cuando actualices el atributo targetSdkVersion
de esta a “19” o un valor superior.
El código subyacente de la clase WebView
y las API relacionadas se actualizó para basarse en un resumen moderno del código fuente de Chromium. Esto aporta diferentes mejoras de rendimiento, y también compatibilidad con funciones HTML5 nuevas y con la depuración remota de tu contenido de WebView
. El alcance de esta actualización implica que, si en tu app se usa WebView
, su comportamiento puede resultar afectado en algunos casos. Aunque los cambios de comportamiento conocidos están documentados y afectan principalmente a tu app solo cuando actualizas el atributo targetSdkVersion
de esta a “19” o un valor superior (la nueva clase WebView
funciona en el “modo no estándar” para proporcionar determinada funcionalidad heredada en apps orientadas hacia el nivel de API 18 y niveles inferiores), es posible que tu app dependa de comportamientos desconocidos de la versión anterior de WebView
.
Por lo tanto, si la app usa WebView
, es importante que la pruebes en Android 4.4 lo antes posible. Además, consulta Migración a WebView en Android 4.4 para obtener información sobre cómo podría verse afectada tu app cuando actualices el atributo targetSdkVersion
a "19" o un valor superior.
Si tu app usa AlarmManager...
Cuando fijas el atributo targetSdkVersion
de tu app en “19” o un valor superior, la alarmas que crees con set()
o setRepeating()
no serán exactas.
Para mejorar la eficiencia energética, Android ahora agrupa las alarmas de todas las apps que tienen lugar en horarios razonablemente similares para que el sistema active el dispositivo una sola vez, en lugar de varias veces, para controlar cada alarma.
Si la alarma no está asociada con un horario exacto, pero sigue siendo importante que se invoque durante un rango horario específico (p. ej., entre las 2 y las 4 p. m.), puedes usar el nuevo método setWindow()
, que acepta un horario “más anterior” para la alarma y un “plazo” posterior a este horario en el cual el sistema debe invocar la alarma.
Si la alarma debe fijarse a un horario exacto (por ejemplo, un recordatorio de un evento del calendario), puedes usar el nuevo método setExact()
.
Este comportamiento de agrupamiento inexacto se aplica únicamente a apps actualizadas. Si fijaste el atributo targetSdkVersion
en “18” o un valor inferior, tus alarmas seguirán comportándose como lo hacían en las versiones anteriores cuando se ejecuten en Android 4.4.
Si tu app sincroniza datos con ContentResolver...
Cuando fijas el atributo targetSdkVersion
de tu app en “19” o un valor superior, la creación de una sincronización con addPeriodicSync()
realizará tus operaciones de sincronización en un intervalo flexible predeterminado de aproximadamente el 4% del período que especifiques. Por ejemplo, si tu frecuencia de sondeo es de 24 horas, la operación de sincronización puede ocurrir dentro de un plazo aproximado de una hora cada día, en lugar de producirse exactamente a la misma hora cada día.
A fin de especificar tu propio intervalo flexible para las operaciones de sincronización, debes comenzar a usar el nuevo método requestSync()
. Para obtener más información, consulta a continuación la sección sobre adaptadores de sincronización.
Este comportamiento de intervalo flexible se aplica solamente a apps actualizadas. Si fijaste el atributo targetSdkVersion
en “18” o un valor inferior, las solicitudes de sincronización existentes seguirán comportándose como lo hacían en las versiones anteriores cuando se ejecuten en Android 4.4.
Framework de impresión
Android ahora incluye un framework completo que permite a los usuarios imprimir documentos con una impresora conectada por Wi-Fi, Bluetooth u otros servicios. El sistema maneja las transacciones entre una app que intenta imprimir un documento y los servicios que envían los trabajos de impresión a una impresora. El framework android.print
proporciona todas las API necesarias para especificar un documento de impresión y enviarlo al sistema para imprimirlo. El contenido determinará la API necesitarás para un trabajo de impresión.
Impresión de contenido genérico
Si deseas imprimir contenido desde la IU como un documento, primero debes crear una subclase de PrintDocumentAdapter
. Dentro de esta clase, debes implementar algunos métodos callback, como onLayout()
, para establecer el diseño según las propiedades de impresión provistas, y onWrite()
, para serializar el contenido imprimible en una clase ParcelFileDescriptor
.
Para escribir tu contenido en la clase ParcelFileDescriptor
, debes pasarle un PDF. Las nuevas PdfDocument
API ofrecen un método práctico para hacer esto proporcionando Canvas
desde getCanvas()
, donde puedes dibujar el contenido imprimible. Luego, escribe PdfDocument
en ParcelFileDescriptor
con el método writeTo()
.
Una vez que definas tu implementación para PrintDocumentAdapter
, puedes ejecutar trabajos de impresión cuando lo solicite el usuario con el método PrintManager
, print()
, que toma PrintDocumentAdapter
como uno de sus argumentos.
Impresión de imágenes
Si deseas imprimir una fotografía u otra imagen de mapa de bits, las API del asistente de la biblioteca de compatibilidad hacen todo el trabajo por ti. Simplemente, crea una instancia nueva de PrintHelper
, fija el modo de la escala con setScaleMode()
y pasa tu Bitmap
a printBitmap()
. Eso es todo. La biblioteca se encargará de toda la interacción restante con el sistema para enviar el mapa de bits a la impresora.
Compilación de servicios de impresión
Como OEM de impresoras, puedes usar el framework android.printservice
para proporcionar interoperabilidad con tus impresoras desde dispositivos Android. Puedes compilar y distribuir servicios de impresión como APK, que los usuarios pueden instalar en sus dispositivos. Una app de servicios de impresión funciona principalmente como un servicio sin periféricos creando subclases de la clase PrintService
, que recibe trabajos de impresión del sistema y comunica los trabajos a las impresoras usando los protocolos correspondientes.
Para obtener más información sobre cómo imprimir el contenido de tu app, consulta Impresión de contenido.
Proveedor de SMS
El proveedor de contenido Telephony
(“proveedor de SMS”) permite que las apps lean y escriban mensajes SMS y MMS en el dispositivo. Incluye tablas para los mensajes SMS y MMS recibidos, en borrador, enviados y pendientes, entre otros elementos.
A partir de Android 4.4, la configuración del sistema permite a los usuarios seleccionar una “app de SMS predeterminada”. Una vez seleccionada, la app de SMS predeterminada puede realizar operaciones de escritura en el proveedor de SMS y solo el proveedor de SMS predeterminado recibe la transmisión SMS_DELIVER_ACTION
cuando el usuario recibe un SMS o la transmisión WAP_PUSH_DELIVER_ACTION
cuando recibe un MMS. La app de SMS predeterminada se encarga de la escritura de los detalles en el proveedor de SMS cuando recibe o envía un mensaje nuevo.
Las otras apps que no se seleccionan como la app de SMS predeterminada solo pueden leer el proveedor de SMS. Sin embargo, también es posible notificar a estas apps cuando llega un SMS nuevo recibiendo la transmisión SMS_RECEIVED_ACTION
, que no se puede anular y se puede enviar a varias apps. Esta transmisión está destinada a apps que, si bien no fueron seleccionadas como la app de SMS predeterminada, deben leer mensajes entrantes especiales; por ejemplo, al realizar la verificación de un número de teléfono.
Para obtener más información, lee la entrada de blog Preparación de apps de SMS para KitKat.
Redes inalámbricas y conectividad
Emulación de tarjeta de host
Las apps de Android ahora emulan tarjetas NFC del protocolo ISO14443-4 (ISO-DEP) que usan APDU para el intercambio de datos (como se especifica en ISO7816-4). Esto permite que un dispositivo habilitado para NFC que ejecuta Android 4.4 emule varias tarjetas NFC al mismo tiempo. También permite que una terminal de pago NFC u otro lector NFC inicie una transacción con la tarjeta NFC correspondiente según el identificador de la aplicación (AID).
Si deseas emular una tarjeta NFC que usa estos protocolos en tu app, crea un componente de servicio basado en la clase HostApduService
. Si, en cambio, tu app usa un elemento seguro para la emulación de la tarjeta, debes crear un servicio basado en la clase OffHostApduService
. Este no tendrá intervención directa en las transacciones, pero es necesario para registrar el AID que el elemento seguro debe controlar.
Para obtener más información, consulta la guía Emulación de tarjetas NFC.
Modo de lector NFC
Un nuevo modo de lector NFC permite que una actividad restrinja toda la actividad de NFC a la lectura exclusiva de los tipos de etiquetas que la actividad busca mientras funciona en segundo plano. Puedes habilitar el modo de lectura para tu actividad con enableReaderMode()
. Esto proporciona una implementación de NfcAdapter.ReaderCallback
que recibe un callback cuando se detectan etiquetas nuevas.
Esta nueva capacidad, sumada a la emulación de tarjeta de host, permite que Android funcione en ambos extremos de una interfaz de pago móvil: un dispositivo funciona como la terminal de pago (un dispositivo que ejecuta una actividad en el modo de lector) y otro como el cliente de pago (un dispositivo que emula una tarjeta NFC).
Transmisores infrarrojos
Cuando uses un dispositivo que incluya un transmisor infrarrojo (IR), podrás trasmitir señales IR con las ConsumerIrManager
API . Para obtener una instancia de ConsumerIrManager
, llama a getSystemService()
con CONSUMER_IR_SERVICE
como argumento. Luego, puedes consultar las frecuencias IR admitidas del dispositivo con getCarrierFrequencies()
y transmitir señales pasando el patrón de frecuencia y señal deseado con transmit()
.
Siempre debes verificar primero si un dispositivo cuenta con un transmisor IR llamando a hasIrEmitter()
. Sin embargo, si tu app es compatible únicamente con dispositivos que tienen uno, debes incluir un elemento <uses-feature>
en el manifiesto para "android.hardware.consumerir"
(FEATURE_CONSUMER_IR
).
Contenido multimedia
Reproducción adaptable
Ahora se ofrece compatibilidad con la reproducción de video adaptable con las MediaCodec
API, lo cual permite el cambio fluido en la resolución durante la reproducción en Surface
. Puedes transmitir los fotogramas de entrada del decodificador de una resolución nueva y la resolución de los búferes de salida cambian sin que haya un salto importante.
Puedes habilitar la reproducción adaptable agregando dos claves a MediaFormat
que especifican la resolución máxima que la app necesita del códec: KEY_MAX_WIDTH
y KEY_MAX_HEIGHT
. Una vez agregadas las claves a tu MediaFormat
, pasa MediaFormat
a la instancia de MediaCodec
con configure()
.
El códec realizará una transición fluida entre las resoluciones que sean iguales o inferiores a estos valores. El códec también puede admitir resoluciones superiores a los máximos especificados (siempre y cuando estén dentro de los límites de los perfiles admitidos). Sin embargo, es posible que las transiciones a resoluciones más grandes no sean fluidas.
Para cambiar la resolución al decodificar video H.264, sigue poniendo en cola los fotogramas con MediaCodec.queueInputBuffer(), pero asegúrate de proporcionar los valores nuevos del conjunto de parámetros de secuencia (SPS) y el conjunto de parámetros de imagen (PPS) junto con el fotograma de actualización instantánea del decodificador (IDR) en un solo búfer.
No obstante, antes de intentar configurar el códec para la reproducción adaptativa, debes verificar que el dispositivo admita esta reproducción llamando a isFeatureSupported(String)
con FEATURE_AdaptivePlayback
.
Nota: La compatibilidad con la reproducción adaptable depende del proveedor. Algunos códecs pueden requerir más memoria para sugerencias de resolución más grandes. Por lo tanto, debes fijar los máximos de resolución según el material de fuente que decodifiques.
Marcas de tiempo de audio a pedido
Para simplificar la sincronización de audio y video, la nueva clase AudioTimestamp
proporciona detalles de la línea del tiempo sobre un “fotograma” específico en una transmisión de audio controlada por AudioTrack
. Para obtener la marca de tiempo más reciente disponible, crea una instancia de un objeto AudioTimestamp
para pasarlo a getTimestamp()
. Si la solicitud de la línea del tiempo tiene éxito, la instancia de AudioTrack
se completa con una posición en unidades de fotogramas, junto con el tiempo estimado en el cual ese fotograma se presentó o se presentará.
Puedes usar el valor de nanoTime
en AudioTimestamp
(que es monotónico) para encontrar el fotograma del video asociado más cercano en comparación con framePosition
, de modo que puedas soltar, duplicar o interpolar fotogramas del video a fin que coincidan con el audio. Como alternativa, puedes determinar el tiempo delta entre el valor de nanoTime
y el tiempo esperado de un fotograma de video futuro (teniendo en cuenta el índice de muestra) para predecir el fotograma de audio que se espera en el mismo momento que un fotograma de video.
Lector de imágenes de superficie
La nueva ImageReader
API te proporciona acceso directo a los búferes de imágenes según se representan en Surface
. Puedes adquirir ImageReader
con el método estático newInstance()
. Luego, llama a getSurface()
para crear una nueva clase Surface
y enviar los datos de la imagen con un productor, como MediaPlayer
o MediaCodec
. Para recibir notificaciones cuando haya imágenes nuevas disponibles de la superficie, implementa la interfaz ImageReader.OnImageAvailableListener
y regístrala con setOnImageAvailableListener()
.
Mientras dibujas contenido en Surface
, ImageReader.OnImageAvailableListener
recibe una llamada para onImageAvailable()
a medida que cada fotograma de imagen nuevo está disponible, lo cual te proporciona la clase ImageReader
correspondiente. Puedes usar ImageReader
para adquirir los datos de imagen del fotograma como un objeto Image
llamando a acquireLatestImage()
o acquireNextImage()
.
El objeto Image
brinda acceso directo a los datos de marca de tiempo, formato, dimensiones y píxeles de la imagen en ByteBuffer
. Sin embargo, para que la clase Image
interprete las imágenes, estas deben recibir formato conforme a uno de los tipos definidos por las constantes de ImageFormat
o PixelFormat
.
Medición de picos y RMS
Ahora puedes consultar el pico y la RMS de la transmisión de audio actual desde Visualizer
creando una instancia nueva de Visualizer.MeasurementPeakRms
y pasándola a getMeasurementPeakRms()
. Cuando llamas a este método, los valores pico y de la RMS de la clase Visualizer.MeasurementPeakRms
determinada se establecen en los últimos valores medidos.
Amplificador de volumen
LoudnessEnhancer
es una subclase nueva de AudioEffect
que te permite aumentar el volumen audible de MediaPlayer
o AudioTrack
. Esto puede ser particularmente útil con el nuevo método getMeasurementPeakRms()
, previamente mencionado, para aumentar el volumen de pistas de audio habladas mientras se reproduce otro medio.
Controladores remotos
En Android 4.0 (nivel de API 14) se incorporaron las RemoteControlClient
API que permiten que las apps de medios consuman eventos de controladores de medios desde clientes remotos, como controles de medios en la pantalla bloqueada. Ahora, las nuevas RemoteController
API te permiten compilar tu propio controlador remoto, lo cual habilita la creación de apps nuevas e innovadoras y periféricos que pueden controlar la reproducción de cualquier app de medios que se integre con RemoteControlClient
.
Para compilar un controlador remoto, puedes implementar la interfaz de usuario como lo desees. Sin embargo, para enviar los eventos de botones de medios a la app de medios del usuario, debes crear un servicio que extienda la clase NotificationListenerService
e implemente la interfaz RemoteController.OnClientUpdateListener
. Es importante usar la clase NotificationListenerService
como base, ya que brinda las restricciones de privacidad correspondientes, las cuales exigen que el usuario habilite tu app como un receptor de notificaciones en la configuración de seguridad del sistema.
La clase NotificationListenerService
incluye un par de métodos abstractos que debes implementar, pero, si solo te interesan los eventos del controlador de medios para manejar la reproducción de medios, puedes dejar la implementación de esos métodos vacía y concentrarte, en cambio, en los métodos RemoteController.OnClientUpdateListener
.
Calificaciones desde controladores remotos
Android 4.4 se apoya en las capacidades existentes de clientes de control remoto (apps que reciben eventos de control de medios con RemoteControlClient
) agregando para los usuarios la capacidad de calificar la pista actual desde el controlador remoto.
La nueva clase Rating
encapsula la información sobre la calificación de un usuario. Una calificación se define por su estilo de calificación (RATING_HEART
, RATING_THUMB_UP_DOWN
, RATING_3_STARS
, RATING_4_STARS
, RATING_5_STARS
o RATING_PERCENTAGE
) y el valor de la calificación que corresponde a ese estilo.
Para permitir que los usuarios califiquen tus pistas desde un controlador remoto:
- Indica que te gustaría exponer la IU de calificación al usuario (si corresponde) agregando el indicador
FLAG_KEY_MEDIA_RATING
ensetTransportControlFlags()
. - Llama a
editMetadata()
para recuperar una claseRemoteControlClient.MetadataEditor
y pásaleRATING_KEY_BY_USER
conaddEditableKey()
. - Luego, especifica el estilo de calificación llamando a
putObject()
, y pasándoleRATING_KEY_BY_USER
como la clave y uno de los estilos de calificación anteriores como el valor.
Para recibir un callback cuando el usuario cambie la calificación desde el controlador remoto, implementa la nueva interfaz RemoteControlClient.OnMetadataUpdateListener
y pasa una instancia a setMetadataUpdateListener()
. Cuando el usuario cambia la calificación, RemoteControlClient.OnMetadataUpdateListener
recibe una llamada para onMetadataUpdate()
, y pasa RATING_KEY_BY_USER
como la clave y un objeto Rating
como el valor.
Subtítulos opcionales
VideoView
ahora admite pistas de subtítulos WebVTT al reproducir videos de transmisión en tiempo real HTTP (HLS) y las muestra según las preferencias de subtítulos opcionales definidas por el usuario en la configuración del sistema.
También puedes proporcionar a VideoView
las pistas de subtítulos WebVTT con el método addSubtitleSource()
. Este método acepta un elemento InputStream
que lleva los datos de subtítulos y un objeto MediaFormat
con el cual se especifica el formato para los datos del subtítulo, que puedes especificar con createSubtitleFormat()
. Estos subtítulos también aparecen sobre el video según las preferencias del usuario.
Si no usas VideoView
para mostrar tu contenido de video, debes procurar la máxima coincidencia posible entre la superposición de subtítulos y las preferencias de subtítulos opcionales del usuario. Una nueva CaptioningManager
API te permite consultar las preferencias de subtítulos opcionales del usuario, incluidos los estilos definidos por CaptioningManager.CaptionStyle
, como el tipo de letra y el color. Si el usuario ajusta algunas preferencias una vez que el video ya se inició, debes recibir los cambios de preferencias registrando una instancia de CaptioningManager.CaptioningChangeListener
para recibir un callback cuando alguna de las preferencias cambie y luego actualizar los subtítulos según sea necesario.
Animación y gráficos
Escenas y transiciones
El nuevo framework android.transition
proporciona API que proporcionan animaciones entre diferentes estados de tu interfaz de usuario. Una característica clave es la capacidad de definir estados diferentes de tu IU, conocidos como “escenas”, creando un diseño separado para cada uno. Cuando desees incluir animaciones entre una escena y otra, ejecuta una “transición” que calcule la animación necesaria para cambiar el diseño de la escena actual a la siguiente.
Generalmente, para realizar una transición entre dos escenas tienes que realizar lo siguiente:
- Especifica la clase
ViewGroup
que contiene los componentes de la IU que desees cambiar. - Especifica el diseño que representa el resultado final del cambio (la siguiente escena).
- Especifica el tipo de transición que debe animar el cambio de diseño.
- Ejecuta la transición.
Puedes usar un objeto Scene
para realizar los pasos 1 y 2. Un objeto Scene
contiene metadatos que describen las propiedades de un diseño necesarias para realizar una transición, como la vista primaria y el diseño de la escena. Puedes crear un objeto Scene
con un constructor de clases o el método estático getSceneForLayout()
.
Luego debes usar TransitionManager
para realizar los pasos 3 y 4. Una manera consiste en pasar tu Scene
al método estático go()
. Con esto se encuentra la vista primaria de la escena en el diseño actual y se realiza una transición en las vistas secundarias para alcanzar el diseño definido por Scene
.
Como alternativa, no es necesario que crees un objeto Scene
. En cambio, puedes llamar a beginDelayedTransition()
, que especifica una clase ViewGroup
en la cual se encuentran las vistas que deseas cambiar. Luego, agrega, quita o reconfigura las vistas objetivo. Una vez que el sistema disponga los cambios según sea necesario, se iniciará una transición para animar todas las vistas afectadas.
Para lograr un control adicional, puedes definir conjuntos de transiciones que deben ocurrir entre escenas predefinidas; para ello, usa un archivo XML en el directorio res/transition/
del proyecto. Dentro de un elemento <transitionManager>
, indica una o más etiquetas <transition>
que en cada caso especifiquen una escena (una referencia a un archivo de diseño) y la transición para la entrada en esa escena o la salida de ella. Luego, amplía este conjunto de transiciones con inflateTransitionManager()
. Usa la clase TransitionManager
mostrada para ejecutar cada transición con transitionTo()
pasando un objeto Scene
representado por una de las etiquetas <transition>
. También puedes definir conjuntos de transiciones de manera programática con las TransitionManager
API.
Cuando especifiques una transición, puedes usar varios tipos predefinidos establecidos por subclases de Transition
, como Fade
y ChangeBounds
. Si no especificas un tipo de transición, el sistema usa AutoTransition
de manera predeterminada. Con esto, se aplicarán automáticamente desvanecimientos, movimientos y cambios el tamaño a las vistas según sea necesario. Además, puedes crear transiciones personalizadas extendiendo cualquiera de estas clases para realizar las animaciones como lo desees. Una transición personalizada puede rastrear los cambios de propiedades que desees y crear cualquier animación de tu preferencia según esos cambios. Por ejemplo, podrías proporcionar una subclase de Transition
que reciba cambios en la propiedad de “rotación” de una vista y luego anime cualquier cambio.
Para obtener más información, consulta la documentación de TransitionManager
.
Pausa del animador
Las Animator
API ahora te permiten pausar y reanudar una animación en curso con los métodos pause()
y resume()
.
Para rastrear el estado de una animación, puedes implementar la interfaz Animator.AnimatorPauseListener
, que brinda callbacks cuando una animación se pone en pausa y se reanuda: pause()
y resume()
. Luego, podrás agrega el receptor a un objeto Animator
con addPauseListener()
.
De manera alternativa, puedes generar una subclase de la clase abstracta AnimatorListenerAdapter
, que ahora incluye implementaciones vacías para los callbacks de pausa y reanudación definidas por Animator.AnimatorPauseListener
.
Mapas de bits reutilizables
Ahora puedes reutilizar los mapas de bits mutables en BitmapFactory
para decodificar cualquier otro mapa de bits, aun cuando el mapa de bits nuevo tenga un tamaño diferente, siempre y cuando el recuento de bytes final del mapa de bits decodificado (disponible desde getByteCount()
) sea inferior o igual al recuento asignado del mapa de bits reutilizado (disponible desde getAllocationByteCount()
). Para obtener más información, consulta inBitmap
.
Las API nuevas para Bitmap
permiten una reconfiguración similar para la reutilización fuera de BitmapFactory
(para la lógica de generación manual de mapas de bits o decodificación personalizada). Ahora puedes establecer las dimensiones del mapa de bits con los métodos setHeight()
y setWidth()
, y especificar una nueva clase Bitmap.Config
con setConfig()
sin afectar la asignación del mapa de bits subyacente. El método reconfigure()
también proporciona una manera conveniente de combinar estos cambios con una llamada.
No obstante, no debes reconfigurar un mapa de bits que el sistema de vista use actualmente, ya que el búfer de píxeles subyacente no se volverá a asignar de una manera predecible.
Contenido del usuario
Framework de acceso a almacenamiento
En versiones anteriores de Android, si deseas que tu app recupere un tipo específico de archivo de otra app, esta debe invocar una intent con la acción ACTION_GET_CONTENT
. Esta acción sigue siendo el método apropiado para solicitar un archivo que desees importar a tu app. Sin embargo, en Android 4.4 se presenta la acción ACTION_OPEN_DOCUMENT
, que permite al usuario seleccionar un archivo de un tipo específico y otorgar a tu app acceso de lectura de largo plazo a ese archivo (posiblemente con acceso de escritura) sin necesidad de importar el archivo a tu app.
Si desarrollas una app que proporciona servicios de almacenamiento para archivos (como un servicio con almacenamiento en la nube), puedes participar en esta IU unificada para seleccionar archivos implementando un proveedor de contenido como una subclase de la nueva clase DocumentsProvider
. En tu subclase de DocumentsProvider
se debe incluir un filtro de intents que acepte la acción PROVIDER_INTERFACE
("android.content.action.DOCUMENTS_PROVIDER"
). Luego, debes implementar los cuatro métodos abstractos en DocumentsProvider
.
queryRoots()
- Con esto, deberá mostrarse una clase
Cursor
que describa todos los directorios raíces de almacenamiento de tus documentos, con columnas definidas enDocumentsContract.Root
. queryChildDocuments()
- Con esto deberá mostrarse una clase
Cursor
que describa todos los archivos en el directorio especificado, con columnas definidas enDocumentsContract.Document
. queryDocument()
- Esto deberá mostrarse una clase
Cursor
que describa el archivo especificado, con columnas definidas enDocumentsContract.Document
. openDocument()
- Con esto, deberá mostrarse una clase
ParcelFileDescriptor
que represente el archivo especificado. El sistema llama a este método una vez que el usuario selecciona un archivo y la app cliente solicita acceso a él llamando aopenFileDescriptor()
.
Para obtener más información, consulta la guía Framework de acceso al almacenamiento.
Acceso a almacenamiento externo
Ahora puedes leer archivos específicos de la app y escribir en ellos en medios de almacenamiento externo secundario, como en el caso de un dispositivo que brinda almacenamiento emulado y en una tarjeta SD. El nuevo método getExternalFilesDirs()
funciona de la misma manera que el método getExternalFilesDir()
existente; la excepción reside en el hecho de que muestra una matriz de objetos File
. Antes del leer una de las rutas de acceso mostradas por este método, o de escribir en ellas, pasa el objeto File
al nuevo método getStorageState()
para verificar que el almacenamiento esté actualmente disponible.
Otros métodos para acceder al directorio de caché y el directorio OBB específicos de app ahora también tienen versiones correspondientes que otorgan acceso a dispositivos de almacenamiento secundario: getExternalCacheDirs()
y getObbDirs()
, respectivamente.
Se considera que la primera entrada en la matriz File
mostrada es el almacenamiento externo primario del dispositivo, que es la misma matriz File
mostrada por los métodos existentes, como getExternalFilesDir()
.
Nota: A partir de Android 4.4, la plataforma ya no requiere que tu app adquiera WRITE_EXTERNAL_STORAGE
o READ_EXTERNAL_STORAGE
cuando necesites acceso únicamente a regiones de almacenamiento externo específicas de tu app usando los métodos anteriores. Sin embargo, se necesitan permisos si deseas acceder a regiones compartibles del almacenamiento externo, proporcionadas por getExternalStoragePublicDirectory()
.
Adaptadores de sincronización
El nuevo método requestSync()
de ContentResolver
simplifica algunos de los procedimientos para definir una solicitud de sincronización de ContentProvider
encapsulando las solicitudes en el nuevo objeto SyncRequest
, que puedes crear con SyncRequest.Builder
. Las propiedades de SyncRequest
brindan la misma funcionalidad que las llamadas de sincronización ContentProvider
existentes, pero agregan la capacidad de especificar que una sincronización se debe abandonar si la red es de uso medido, mediante la habilitación de setDisallowMetered()
.
Entrada del usuario
Tipos de sensores nuevos
El nuevo sensor TYPE_GEOMAGNETIC_ROTATION_VECTOR
proporciona datos de vectores de rotación según un magnetómetro, que es una alternativa útil al sensor TYPE_ROTATION_VECTOR
cuando un giroscopio no está disponible o cuando se usa con eventos de sensores por lotes para registrar la orientación del dispositivo mientras el teléfono está en suspensión. Este sensor requiere menos energía que TYPE_ROTATION_VECTOR
, pero puede estar expuesto a datos de eventos irrelevantes y es más eficaz cuando el usuario está al aire libre.
Android ahora también es compatible con sensores de pasos incorporados en el hardware:
TYPE_STEP_DETECTOR
- Este sensor activa un evento cada vez que el usuario da un paso. Con cada paso, el sensor genera un evento con un valor de 1,0 y una marca de tiempo que indica el momento en que se produjo el paso.
TYPE_STEP_COUNTER
- Este sensor también activa un evento con cada paso detectado, pero como alternativa genera la cantidad total de pasos acumulados desde que una app registró por primera vez este sensor.
Ten en cuenta que estos dos sensores no siempre presentan los mismos resultados. Los eventos TYPE_STEP_COUNTER
se producen con una latencia mayor que aquellos que se generan desde TYPE_STEP_DETECTOR
, pero eso se debe a que el algoritmo TYPE_STEP_COUNTER
realiza un mayor procesamiento para eliminar los falsos positivos. Por lo tanto, TYPE_STEP_COUNTER
puede ser más lento para generar los eventos, pero los resultados deben ser más precisos.
Ambos detectores de pasos dependen del hardware (Nexus 5 es el primer dispositivo que los admite), por lo que debes controlar la disponibilidad con hasSystemFeature()
, usando las constantes FEATURE_SENSOR_STEP_DETECTOR
y FEATURE_SENSOR_STEP_COUNTER
.
Eventos de sensores por lotes
Para administrar mejor la energía de tu dispositivo, las SensorManager
API ahora te permiten especificar la frecuencia a la que te gustaría que el sistema proporcione lotes de eventos de sensores a tu app. Esto no reduce la cantidad de eventos de sensores reales disponibles para tu app durante un período determinado, sino más bien la frecuencia a la que el sistema llama a SensorEventListener
con actualizaciones de los sensores. Es decir, en lugar de proporcionar cada evento a tu app en el momento en el que se produce, el sistema acumula todos los eventos que tienen lugar en un período y luego los entrega a tu app de una sola vez.
Para el tratamiento por lotes, la clase SensorManager
agrega dos versiones nuevas del método registerListener()
que te permiten especificar la “latencia de informe máxima”. Este parámetro nuevo especifica el retraso máximo que SensorEventListener
tolerará para proporcionar eventos de sensores nuevos. Por ejemplo, si especificas una latencia para el lote de un minuto, el sistema proporcionará el conjunto reciente de eventos por lotes en un intervalo no superior a un minuto haciendo llamadas consecutivas al método onSensorChanged()
, una vez por cada evento que se incluyó en el lote. El retraso de los eventos de sensores nunca superará el valor de la latencia de informe máxima, pero pueden presentarse antes si otras apps solicitan una latencia más corta para el mismo sensor.
No obstante, ten en cuenta que el sensor proporcionará a la app los eventos por lotes según la latencia de información solamente cuando la CPU esté activa. Si bien un sensor de hardware compatible con los lotes seguirá recolectando los eventos de sensores mientras la CPU está suspendida, no la activará para proporcionar a la app los eventos por lotes. Cuando se acabe la memoria para eventos del sensor, este comenzará a dejar de lado los eventos más antiguos para guardar los más nuevos. Puedes evitar perder eventos activando el dispositivo antes de que la memoria del sensor se complete y luego llamar a flush()
para capturar el último lote de eventos. A fin de calcular el momento en que se llenará la memoria a fin de vaciarla, llama a getFifoMaxEventCount()
para conocer la cantidad máxima de eventos de sensores que puede guardar y divide esa cantidad por el índice en el que tu app desee cada evento. Usa ese cálculo para configurar alarmas de activación con AlarmManager
que invoquen Service
(que implementa SensorEventListener
) para vaciar el sensor.
Nota: No todos los dispositivos admiten el procesamiento por lotes de eventos de sensores porque se necesita compatibilidad con el sensor de hardware. Sin embargo, a partir de a Android 4.4 siempre debes usar los nuevos métodos registerListener()
, ya que si el dispositivo no admite el procesamiento por lotes el sistema ignorará correctamente el argumento de latencia de lote y proporcionará los eventos de sensor en tiempo real.
Identidades de los controladores
Android ahora identifica cada controlador conectado con un número entero único que puedes consultar con getControllerNumber()
, lo cual facilita la asociación de cada controlador a un jugador diferente en un juego. El número de cada controlador puede cambiar si el usuario lo desconecta, conecta o reconfigura, por lo que debes rastrear el número de controlador que corresponde a cada dispositivo de entrada registrando una instancia de InputManager.InputDeviceListener
. Luego, llama a getControllerNumber()
para cada InputDevice
cuando se produzca un cambio.
Ahora, los dispositivos conectados también pueden proporcionar ID de productos y proveedores que estén disponibles a través de getProductId()
y getVendorId()
. Si debes modificar las asignaciones de claves según el conjunto de claves disponible en un dispositivo, puedes consultar el dispositivo para controlar si determinadas claves están disponibles con hasKeys(int...)
.
Interfaz de usuario
Modo de pantalla completa envolvente
Para que tu app tenga un diseño que ocupe toda la pantalla, el nuevo indicador SYSTEM_UI_FLAG_IMMERSIVE
para setSystemUiVisibility()
(cuando se combina con SYSTEM_UI_FLAG_HIDE_NAVIGATION
) permite un nuevo modo de pantalla completa envolvente. Mientras el modo de pantalla completa envolvente está habilitado, la actividad continúa recibiendo todos los eventos táctiles. El usuario puede revelar las barras del sistema con un deslizamiento hacia adentro en la región donde normalmente aparecen las barras del sistema. Con esto se borra el indicador SYSTEM_UI_FLAG_HIDE_NAVIGATION
(y el indicador SYSTEM_UI_FLAG_FULLSCREEN
, si se aplica) para que las barras del sistema permanezcan visibles. Sin embargo, si deseas que las barras del sistema se oculten nuevamente después de unos momentos, puedes usar el indicador SYSTEM_UI_FLAG_IMMERSIVE_STICKY
.
Barras de sistema translúcidas
Ahora puedes hacer que las barras de sistema sean parcialmente traslúcidas con temas nuevos: Theme.Holo.NoActionBar.TranslucentDecor
y Theme.Holo.Light.NoActionBar.TranslucentDecor
. Habilitando las barras de sistema traslúcidas, el diseño completará el área detrás de las barras de sistema, por lo cual también debes habilitar fitsSystemWindows
para la parte del diseño que las barras de sistema no deben cubrir.
Si creas un tema personalizado, configura uno de estos temas como el tema primario o incluye las propiedades de estilo windowTranslucentNavigation
y windowTranslucentStatus
en tu tema.
Receptor de notificaciones mejorado
En Android 4.3 se agregaron las NotificationListenerService
API, lo cual permite que las apps reciban información sobre notificaciones nuevas a medida que el sistema las publica. En Android 4.4, los receptores de notificaciones pueden recuperar metadatos adicionales de la notificación e información completa sobre las acciones de esta:
En el nuevo campo Notification.extras
se incluye una clase Bundle
para proporcionar metadatos adicionales del generador de notificaciones, como EXTRA_TITLE
y EXTRA_PICTURE
. La nueva clase Notification.Action
define las características de una acción adjunta a la notificación, que puedes recuperar del nuevo campo actions
.
Duplicación de elementos de diseño para diseños RTL
En versiones anteriores de Android, si en tu app se incluyen imágenes que deben cambiar la orientación horizontal para diseños de derecha a izquierda, debes incluir una imagen duplicada en un directorio de recursos drawables-ldrtl/
. Ahora, el sistema puede duplicar imágenes automáticamente habilitando el atributo autoMirrored
en un recurso de elementos de diseño o llamando a setAutoMirrored()
. Cuando está habilitada, la clase Drawable
se duplica automáticamente si el diseño tiene orientación de derecha a izquierda.
Accesibilidad
La clase View
ahora te permite declarar “regiones activas” para secciones de la IU que se actualizan dinámicamente con contenido de texto nuevo. Para ello, agrega el nuevo atributo accessibilityLiveRegion
a tu diseño XML o llama a setAccessibilityLiveRegion()
. Por ejemplo, una pantalla de acceso con un campo de texto en la que se muestra una notificación de “contraseña incorrecta” debe marcarse como región activa para que el lector de pantalla transmita el mensaje cuando cambie.
Las apps que proporcionan un servicio de accesibilidad ahora también pueden mejorar sus capacidades con API nuevas que brindan información sobre grupos de vistas, como vistas de listas o de cuadrícula, con AccessibilityNodeInfo.CollectionInfo
y AccessibilityNodeInfo.CollectionItemInfo
.
Permisos para apps
Tu app debe solicitar los siguientes permisos nuevos con la etiqueta <uses-permission>
para usar determinadas API nuevas:
INSTALL_SHORTCUT
- Permite que una aplicación instale un acceso directo en Launcher.
UNINSTALL_SHORTCUT
- Permite que una aplicación desinstale un acceso directo de Launcher.
TRANSMIT_IR
- Permite que una aplicación use el transmisor IR del dispositivo, si se encuentra disponible.
Nota: A partir de Android 4.4, la plataforma ya no requiere que tu app adquiera WRITE_EXTERNAL_STORAGE
o READ_EXTERNAL_STORAGE
cuando desees acceder a regiones del almacenamiento externo específicas de tu app usando métodos como getExternalFilesDir()
. Sin embargo, los permisos siguen siendo obligatorios si deseas acceder a regiones compartibles del almacenamiento externo, proporcionadas por getExternalStoragePublicDirectory()
.
Funciones del dispositivo
A continuación, se detallan las funciones nuevas del dispositivo que puedes declarar con la etiqueta <uses-feature>
para declarar los requisitos de tu app y habilitar el filtrado en Google Play o realizar verificaciones durante el tiempo de ejecución:
FEATURE_CONSUMER_IR
- El dispositivo puede comunicarse con dispositivos IR del consumidor.
FEATURE_DEVICE_ADMIN
- El dispositivo admite la aplicación de políticas de dispositivo mediante administradores de dispositivos.
FEATURE_NFC_HOST_CARD_EMULATION
- El dispositivo admite la emulación de tarjetas NFC basada en host.
FEATURE_SENSOR_STEP_COUNTER
- En el dispositivo se incluye un contador de pasos de hardware.
FEATURE_SENSOR_STEP_DETECTOR
- En el dispositivo se incluye un detector de pasos de hardware.
Para obtener una vista detallada de todos los cambios de las API en Android 4.4, consulta el Informe de diferencias de las API.