API de Android 4.4

Nivel de API: 19

Android 4.4 (KITKAT) es una nueva versión de la plataforma de Android que ofrece nuevas funciones a los usuarios y desarrolladores de apps. En este documento se ofrece una introducción a las API nuevas más distinguidas.

Como desarrollador de apps, debes descargar la imagen del sistema Android 4.4 y la plataforma del SDK de SDK Manager lo antes posible. Si no tienes un dispositivo con Android 4.4 en el que puedas probar la app, usa la imagen del sistema de Android 4.4 para probarla en el emulador de Android. Luego, compila tus apps con la plataforma Android 4.4 para comenzar a usar las APIs más recientes.

Actualiza el nivel de tu API de destino

A fin de optimizar mejor tu app para dispositivos que ejecutan Android 4.4, debes configurar tu targetSdkVersion como "19", instalarla en una imagen del sistema de Android 4.4, probarla y, luego, publicar una actualización con este cambio.

Puedes usar APIs en Android 4.4 y, al mismo tiempo, admitir versiones anteriores si agregas condiciones a tu código que verifiquen el nivel de API del sistema antes de ejecutar APIs no compatibles con tu minSdkVersion. Para obtener más información sobre cómo mantener la retrocompatibilidad, consulta Cómo brindar compatibilidad con diferentes versiones de plataforma.

Para obtener más información sobre cómo funcionan los niveles de API, consulta ¿Qué es un nivel de API?

Importantes cambios en los comportamientos

Si publicaste anteriormente una app para Android, ten en cuenta que esta podría verse afectada por los cambios en Android 4.4.

Si tu app realiza operaciones de lectura desde un medio de almacenamiento externo...

Tu app no puede leer archivos compartidos en el almacenamiento externo cuando se ejecuta en Android 4.4, a menos que tenga el permiso READ_EXTERNAL_STORAGE. Es decir, ya no se puede acceder a los archivos dentro del directorio que muestra getExternalStoragePublicDirectory() sin el permiso. Sin embargo, si solo necesitas acceder a los directorios específicos de tu app que proporciona getExternalFilesDir(), no necesitas el permiso READ_EXTERNAL_STORAGE.

Si tu app usa WebView...

Es posible que tu app se comporte de manera diferente cuando se ejecuta en Android 4.4, en especial cuando actualizas targetSdkVersion a "19" o una versión posterior.

El código subyacente de la clase WebView y las APIs relacionadas se actualizó para basarse en una instantánea moderna del código fuente de Chromium. Esto aporta una variedad de mejoras en el rendimiento, compatibilidad con nuevas funciones de HTML5 y compatibilidad con la depuración remota de tu contenido de WebView. El alcance de esta actualización implica que, si tu app usa WebView, su comportamiento puede verse afectado en algunos casos. Si bien los cambios de comportamiento conocidos están documentados y afectan principalmente a tu app solo cuando actualizas targetSdkVersion de tu app a "19" o una versión posterior, el nuevo WebView funciona en "modo no estándar" para proporcionar algunas funciones heredadas en apps orientadas al nivel de API 18 y versiones anteriores, es posible que tu app dependa de comportamientos desconocidos de la versión anterior de WebView.

Por lo tanto, si tu app existente usa WebView, es importante que realices pruebas en Android 4.4 lo antes posible y consultes Cómo migrar a WebView en Android 4.4 para obtener información sobre cómo podría verse afectada tu app cuando actualices tu targetSdkVersion a "19" o una versión posterior.

Si tu app usa AlarmManager...

Cuando configuras la targetSdkVersion de tu app en "19" o un valor superior, las 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 tu alarma no está asociada con una hora exacta, pero es importante que se invoque durante un intervalo de tiempo específico (como entre las 2 p.m. y las 4 p.m.), puedes usar el nuevo método setWindow(), que acepta una hora "más antigua" para la alarma y una "ventana" de tiempo después de la hora más temprana en la que el sistema debe invocar la alarma.

Si la alarma debe fijarse a una hora exacta (por ejemplo, para un recordatorio de un evento de calendario), puedes usar el nuevo método setExact().

Este comportamiento de agrupamiento inexacto se aplica únicamente a apps actualizadas. Si estableciste targetSdkVersion en "18" o un valor inferior, las 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 configures la 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 dentro de 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.

Para 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 la siguiente sección sobre adaptadores de sincronización.

Este comportamiento de intervalo flexible se aplica solamente a apps actualizadas. Si estableciste targetSdkVersion en "18" o versiones anteriores, las solicitudes de sincronización existentes seguirán comportándose como lo hacían en 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 APIs necesarias para especificar un documento de impresión y entregarlo al sistema para que lo imprima. 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 de devolución de llamada, incluidos onLayout() para establecer tu diseño según las propiedades de impresión proporcionadas y onWrite() para serializar tu contenido imprimible en un ParcelFileDescriptor.

Para escribir tu contenido en ParcelFileDescriptor, debes pasarle un PDF. Las nuevas APIs de PdfDocument ofrecen una manera conveniente de hacerlo, ya que proporcionan un Canvas de getCanvas(), en el que puedes dibujar tu contenido imprimible. Luego, escribe el PdfDocument en ParcelFileDescriptor con el método writeTo().

Una vez que hayas definido tu implementación para PrintDocumentAdapter, puedes ejecutar trabajos de impresión a pedido del usuario mediante 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, establece el modo de escala con setScaleMode() y, luego, 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 interfaz gráfica mediante la subclasificación de la clase PrintService, que recibe los trabajos de impresión del sistema y los comunica a las impresoras con los protocolos adecuados.

Para obtener más información sobre cómo imprimir el contenido de tu app, consulta Cómo imprimir contenido.

Proveedor de SMS

El proveedor de contenido Telephony (el "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 que los usuarios seleccionen una "app de SMS predeterminada". Una vez seleccionada, solo la app de SMS predeterminada puede escribir en el proveedor de SMS, y solo la app de SMS predeterminada recibe la transmisión SMS_DELIVER_ACTION cuando el usuario recibe un SMS o 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.

Otras apps que no se hayan seleccionado como la app de SMS predeterminada solo pueden leer el proveedor de SMS. Sin embargo, es posible que también se les notifique cuando llegue un nuevo SMS escuchando la transmisión de SMS_RECEIVED_ACTION, que es una transmisión no anulable que puede enviarse 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 Cómo preparar tus 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. Por el contrario, si la app usa un elemento seguro para la emulación de tarjetas, debes crear un servicio basado en la clase OffHostApduService, que no estará involucrado directamente en las transacciones, pero es necesario para registrar los AID que debe controlar el Elemento seguro.

Para obtener más información, lee 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(), lo que proporciona una implementación de NfcAdapter.ReaderCallback que recibe una devolución de llamada cuando se detectan etiquetas nuevas.

Esta nueva capacidad, junto con 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 terminal de pago (un dispositivo que ejecuta una actividad en modo de lectura) y otro como cliente de pago (un dispositivo que emula una tarjeta NFC).

Transmisores infrarrojos

Cuando se ejecuta en un dispositivo que incluye un transmisor infrarrojo (IR), ahora puedes transmitir señales IR con las APIs de ConsumerIrManager. 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 incluye un transmisor IR llamando a hasIrEmitter(). Sin embargo, si tu app solo es compatible con dispositivos que tienen uno, debes incluir un elemento <uses-feature> en tu manifiesto para "android.hardware.consumerir" (FEATURE_CONSUMER_IR).

Multimedia

Reproducción adaptable

La compatibilidad con la reproducción de video adaptable ahora está disponible con las APIs de MediaCodec, lo que permite un cambio fluido en la resolución durante la reproducción en un Surface. Puedes proporcionar los fotogramas de entrada del decodificador de una resolución nueva y la resolución de los búferes de salida cambia sin un intervalo significativo.

Para habilitar la reproducción adaptable, agrega dos claves a MediaFormat que especifiquen la resolución máxima que tu app requiere del códec: KEY_MAX_WIDTH y KEY_MAX_HEIGHT. Agrega estos elementos a tu MediaFormat, pasa MediaFormat a tu 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.

Sin embargo, antes de intentar configurar tu códec para la reproducción adaptable, debes verificar que el dispositivo la admita mediante una llamada 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 facilitar la sincronización de audio y video, la nueva clase AudioTimestamp proporciona detalles de la línea de 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 y pásalo a getTimestamp(). Si la solicitud de la marca de tiempo se realiza correctamente, se completa la instancia de AudioTrack con una posición en unidades de fotogramas, junto con el tiempo estimado en el que ese fotograma se presentó o se presentará.

Puedes usar el valor de nanoTime en AudioTimestamp (que es monotónico) para encontrar el fotograma de video asociado más cercano en comparación con framePosition, de modo que puedas soltar, duplicar o interpolar fotogramas de video para 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 la tasa de muestreo) para predecir qué fotograma de audio se espera en el mismo momento que un fotograma de video.

Lector de imágenes de superficie

La nueva API de ImageReader te proporciona acceso directo a los búferes de imagen mientras se renderizan en Surface. Puedes adquirir un objeto ImageReader con el método estático newInstance(). Luego, llama a getSurface() para crear un Surface nuevo y enviar los datos de imágenes con un productor, como MediaPlayer o MediaCodec. Si quieres recibir notificaciones cuando haya imágenes nuevas disponibles en la plataforma, implementa la interfaz de ImageReader.OnImageAvailableListener y regístrala con setOnImageAvailableListener().

Ahora, a medida que dibujas contenido en tu Surface, ImageReader.OnImageAvailableListener recibe una llamada a onImageAvailable() a medida que cada fotograma de imagen nuevo está disponible, lo que te proporciona el ImageReader correspondiente. Puedes usar ImageReader para adquirir los datos de imagen del marco como un objeto Image llamando a acquireLatestImage() o acquireNextImage().

El objeto Image proporciona acceso directo a la marca de tiempo, el formato, las dimensiones y los datos de píxeles de la imagen en un ByteBuffer. Sin embargo, para que la clase Image interprete tus imágenes, estas deben tener un formato conforme a uno de los tipos definidos por las constantes en 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 nueva instancia de Visualizer.MeasurementPeakRms y pasándola a getMeasurementPeakRms(). Cuando llamas a este método, los valores máximos y de RMS de la Visualizer.MeasurementPeakRms determinada se establecen en los últimos valores medidos.

Amplificador de volumen

LoudnessEnhancer es una nueva subclase de AudioEffect que te permite aumentar el volumen audible de MediaPlayer o AudioTrack. Esto puede ser especialmente útil junto con el nuevo método getMeasurementPeakRms() mencionado anteriormente, para aumentar el volumen de las pistas de audio habladas mientras se reproduce otro contenido multimedia.

Controladores remotos

Android 4.0 (nivel de API 14) introdujo las API de RemoteControlClient que permiten que las apps de música consuman eventos de controladores multimedia de clientes remotos, como controles multimedia en la pantalla de bloqueo. Ahora, las nuevas APIs de RemoteController te permiten compilar tu propio control remoto, lo que permite la creación de apps y periféricos nuevos e innovadores que pueden controlar la reproducción de cualquier app de música que se integre con RemoteControlClient.

Para compilar un control remoto, puedes implementar tu interfaz de usuario de la manera que desees, pero para enviar los eventos de botones de medios a la app de música del usuario, debes crear un servicio que extienda la clase NotificationListenerService e implemente la interfaz RemoteController.OnClientUpdateListener. Es importante usar NotificationListenerService como base, ya que proporciona las restricciones de privacidad adecuadas, que requieren que los usuarios habiliten tu app como un objeto de escucha 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 preocupan los eventos del controlador multimedia para controlar la reproducción multimedia, puedes dejar tu implementación vacía para esos métodos y, en su lugar, enfocarte en los métodos RemoteController.OnClientUpdateListener.

Calificaciones desde controladores remotos

Android 4.4 se basa en las capacidades existentes de los clientes de control remoto (apps que reciben eventos de control de medios con RemoteControlClient) y agrega la capacidad para que los usuarios califiquen la pista actual desde el control remoto.

La nueva clase Rating contiene 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:

Para recibir una devolución de llamada cuando el usuario cambie la calificación desde el control remoto, implementa la nueva interfaz RemoteControlClient.OnMetadataUpdateListener y pasa una instancia a setMetadataUpdateListener(). Cuando el usuario cambia la calificación, tu RemoteControlClient.OnMetadataUpdateListener recibe una llamada a onMetadataUpdate() y pasa RATING_KEY_BY_USER como la clave y un objeto Rating como el valor.

Subtítulos

VideoView ahora admite pistas de subtítulos WebVTT cuando se reproducen videos HTTP de transmisión en vivo (HLS), lo que muestra la pista de subtítulos según las preferencias de subtítulos que definió el usuario en la configuración del sistema.

También puedes proporcionar a VideoView las pistas de subtítulos de WebVTT mediante el método addSubtitleSource(). Este método acepta un InputStream que lleva los datos de subtítulos y un objeto MediaFormat que especifica el formato para los datos de subtítulos, 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 el contenido de tu video, la superposición de subtítulos debe coincidir con las preferencias de subtítulos del usuario de la mejor manera posible. Una nueva API de CaptioningManager te permite consultar las preferencias de subtítulos del usuario, incluidos los estilos definidos por CaptioningManager.CaptionStyle, como el tipo de letra y el color. En caso de que el usuario ajuste algunas preferencias cuando se inicie el video, debes escuchar los cambios en las preferencias registrando una instancia de CaptioningManager.CaptioningChangeListener para recibir una devolución de llamada cuando cambie alguna de las preferencias y, luego, actualizar los subtítulos según sea necesario.

Animación y gráficos

Escenas y transiciones

El nuevo framework android.transition proporciona APIs que facilitan animaciones entre diferentes estados de tu interfaz de usuario. Una función clave es la capacidad de definir estados distintos de tu IU, conocidos como "escenas", creando un diseño separado para cada uno. Cuando quieras animar de una escena a otra, ejecuta una "transición", que calcula 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:

  1. Especifica el ViewGroup que contiene los componentes de la IU que quieras cambiar.
  2. Especifica el diseño que representa el resultado final del cambio (la siguiente escena).
  3. Especifica el tipo de transición que debe animar el cambio de diseño.
  4. Ejecuta la transición.

Puedes usar un objeto Scene para realizar los pasos 1 y 2. Un Scene contiene metadatos que describen las propiedades de un diseño que son necesarias para realizar una transición, incluida la vista superior de la escena y el diseño de la escena. Puedes crear un Scene con un constructor de clase o el método estático getSceneForLayout().

Luego, debes usar TransitionManager para completar los pasos 3 y 4. Una forma es pasar tu Scene al método estático go(). De esta manera, se encuentra la vista superior 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 el Scene.

Como alternativa, no necesitas crear un objeto Scene. En su lugar, puedes llamar a beginDelayedTransition() y especificar un ViewGroup que contenga 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 obtener un control adicional, puedes definir conjuntos de transiciones que deberían ocurrir entre escenas predefinidas mediante un archivo XML en el directorio res/transition/ de tu proyecto. Dentro de un elemento <transitionManager>, especifica una o más etiquetas <transition> que indiquen una escena (una referencia a un archivo de diseño) y la transición que se aplicará cuando ingreses a esa escena o salgas de ella. Luego, aumenta este conjunto de transiciones con inflateTransitionManager(). Usa el TransitionManager que se muestra para ejecutar cada transición con transitionTo() y pasa un Scene representado por una de las etiquetas <transition>. También puedes definir conjuntos de transiciones de manera programática con las APIs de TransitionManager.

Cuando especificas una transición, puedes usar varios tipos predefinidos definidos por subclases de Transition, como Fade y ChangeBounds. Si no especificas un tipo de transición, el sistema usa AutoTransition de forma predeterminada, que automáticamente atenúa, mueve y cambia el tamaño de 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 hacer un seguimiento de cualquier cambio de propiedad que desees y crear cualquier animación que desees en función de esos cambios. Por ejemplo, puedes proporcionar una subclase de Transition que escuche los 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 APIs de Animator ahora te permiten pausar y reanudar una animación en curso con los métodos pause() y resume().

Para realizar un seguimiento del estado de una animación, puedes implementar la interfaz Animator.AnimatorPauseListener, que proporciona devoluciones de llamada cuando se pausa y reanuda una animación: pause() y resume(). Luego, agrega el objeto de escucha a un objeto Animator con addPauseListener().

Como alternativa, puedes crear una subclase de la clase abstracta AnimatorListenerAdapter, que ahora incluye implementaciones vacías para las devoluciones de llamada de pausa y reanudación definidas por Animator.AnimatorPauseListener.

Mapas de bits reutilizables

Ahora puedes reutilizar cualquier mapa de bits mutable en BitmapFactory para decodificar cualquier otro mapa de bits, incluso cuando el mapa de bits nuevo tiene un tamaño diferente, siempre que el recuento de bytes resultante del mapa de bits decodificado (disponible en getByteCount()) sea menor o igual que el recuento de bytes asignado del mapa de bits reutilizado (disponible en getAllocationByteCount()). Para obtener más información, consulta inBitmap.

Las nuevas APIs para Bitmap permiten una reconfiguración similar para reutilizarla fuera de BitmapFactory (para la generación manual de mapas de bits o la lógica de decodificación personalizada). Ahora puedes establecer las dimensiones de un mapa de bits con los métodos setHeight() y setWidth(), y especificar un nuevo Bitmap.Config con setConfig() sin afectar la asignación del mapa de bits subyacente. El método reconfigure() también proporciona una forma conveniente de combinar estos cambios con una llamada.

Sin embargo, no debes reconfigurar un mapa de bits que el sistema de vista usa actualmente, ya que el búfer de píxeles subyacente no se reasignará de manera predecible.

Contenido del usuario

Framework de acceso a almacenamiento

En versiones anteriores de Android, si quieres que tu app recupere un tipo específico de archivo de otra app, esta debe invocar un intent con la acción ACTION_GET_CONTENT. Esta acción sigue siendo la manera adecuada de solicitar un archivo que deseas importar a tu app. Sin embargo, Android 4.4 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 a largo plazo a ese archivo (posiblemente con acceso de escritura) sin importar el archivo a tu app.

Si estás desarrollando una app que proporciona servicios de almacenamiento para archivos (como un servicio de almacenamiento en la nube), puedes participar en esta IU unificada para seleccionar archivos mediante la implementación de un proveedor de contenido como una subclase de la nueva clase DocumentsProvider. Tu subclase de DocumentsProvider 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()
Debe mostrar un Cursor que describa todos los directorios raíz de tu almacenamiento de documentos con las columnas definidas en DocumentsContract.Root.
queryChildDocuments()
Debe mostrar una Cursor que describa todos los archivos del directorio especificado con columnas definidas en DocumentsContract.Document.
queryDocument()
Debe mostrar un Cursor que describa el archivo especificado con columnas definidas en DocumentsContract.Document.
openDocument()
Debe mostrar una 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 a openFileDescriptor().

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 igual que el método getExternalFilesDir() existente, excepto que muestra un array de objetos File. Antes de leer o escribir en cualquiera de las rutas de acceso que muestra este método, pasa el objeto File al nuevo método getStorageState() para verificar que el almacenamiento esté disponible en ese momento.

Otros métodos para acceder al directorio de caché y al directorio OBB específicos de la app ahora también tienen versiones correspondientes que proporcionan acceso a dispositivos de almacenamiento secundario: getExternalCacheDirs() y getObbDirs(), respectivamente.

La primera entrada del array de File que se muestra se considera el almacenamiento externo principal del dispositivo, que es la misma que la File que muestran 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 necesitas acceder solo a regiones específicas de tu app del almacenamiento externo con los métodos anteriores. Sin embargo, los permisos son obligatorios si quieres acceder a las regiones compartibles del almacenamiento externo, que proporciona getExternalStoragePublicDirectory().

Adaptadores de sincronización

El nuevo método requestSync() en ContentResolver simplifica algunos de los procedimientos a fin de definir una solicitud de sincronización para tu ContentProvider mediante el encapsulamiento de solicitudes en el nuevo objeto SyncRequest, que puedes crear con SyncRequest.Builder. Las propiedades de SyncRequest proporcionan la misma funcionalidad que las llamadas de sincronización ContentProvider existentes, pero agregan la capacidad de especificar que una sincronización se debe interrumpir 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 vectoriales de rotación basados en un magnetómetro, que es una alternativa útil al sensor TYPE_ROTATION_VECTOR cuando no hay un giroscopio disponible o cuando se usa con eventos de sensores por lotes para registrar la orientación del dispositivo mientras el teléfono está suspendido. Este sensor requiere menos energía que TYPE_ROTATION_VECTOR, pero puede ser propenso a datos de eventos ruidosos 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 del usuario, el sensor genera un evento con un valor de 1.0 y una marca de tiempo que indica cuándo ocurrió el paso.
TYPE_STEP_COUNTER
Este sensor también activa un evento con cada paso detectado, pero proporciona la cantidad total acumulada de pasos desde que una app registró este sensor por primera vez.

Ten en cuenta que estos dos sensores de pasos no siempre producen los mismos resultados. Los eventos TYPE_STEP_COUNTER ocurren con una latencia más alta que los de TYPE_STEP_DETECTOR, pero esto se debe a que el algoritmo TYPE_STEP_COUNTER realiza más procesamiento para eliminar los falsos positivos. Por lo tanto, es posible que TYPE_STEP_COUNTER tarde más en entregar eventos, pero los resultados deberían ser más precisos.

Ambos sensores de pasos dependen del hardware (Nexus 5 es el primer dispositivo que los admite), por lo que debes verificar 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 del dispositivo, las APIs de SensorManager ahora te permiten especificar la frecuencia a la que deseas que el sistema envíe 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 la frecuencia con la que el sistema llama a tu SensorEventListener con actualizaciones del sensor. 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 proporcionar el procesamiento 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 tolerará tu SensorEventListener para la entrega de nuevos eventos de sensores. Por ejemplo, si especificas una latencia de lote de un minuto, el sistema entregará el conjunto reciente de eventos por lotes en un intervalo no superior a un minuto mediante llamadas consecutivas a tu método onSensorChanged(), una vez por cada evento que se agrupó en lotes. 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.

Sin embargo, ten en cuenta que el sensor entregará a tu app los eventos por lotes según la latencia de tu informe solo 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. Para evitar perder eventos, activa el dispositivo antes de que el sensor llene su memoria y, luego, llame a flush() para capturar el último lote de eventos. Para calcular cuándo se llenará la memoria y cuándo se debe vaciar, llama a getFifoMaxEventCount() para obtener la cantidad máxima de eventos del sensor que puede guardar y divide esa cantidad por la velocidad a la que tu app desea cada evento. Usa ese cálculo para establecer alarmas de activación con AlarmManager que invoquen tu Service (que implementa SensorEventListener) para vaciar el sensor.

Nota: No todos los dispositivos admiten el procesamiento por lotes de eventos de sensores porque requiere compatibilidad con el sensor de hardware. Sin embargo, a partir de 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 del lote y entregará eventos de sensores en tiempo real.

Identidades de los controladores

Android ahora identifica cada control conectado con un número entero único que puedes consultar con getControllerNumber(), lo que facilita la asociación de cada control a un jugador diferente en un juego. El número de cada control puede cambiar si el usuario lo desconecta, conecta o reconfigura, por lo que debes registrar una instancia de InputManager.InputDeviceListener para realizar un seguimiento del número de control que corresponde a cada dispositivo de entrada. Luego, llama a getControllerNumber() para cada InputDevice cuando se produzca un cambio.

Los dispositivos conectados ahora también proporcionan IDs de productos y proveedores que están disponibles en getProductId() y getVendorId(). Si necesitas modificar tus asignaciones de claves según el conjunto de claves disponible en un dispositivo, puedes consultarlo para verificar si ciertas claves están disponibles con hasKeys(int...).

Interfaz de usuario

Modo de pantalla completa envolvente

A fin de proporcionar a tu app un diseño que ocupe toda la pantalla, la nueva marca SYSTEM_UI_FLAG_IMMERSIVE para setSystemUiVisibility() (cuando se combina con SYSTEM_UI_FLAG_HIDE_NAVIGATION) habilita 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. Esto borra la marca SYSTEM_UI_FLAG_HIDE_NAVIGATION (y la marca 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 la marca SYSTEM_UI_FLAG_IMMERSIVE_STICKY.

Barras de sistema translúcidas

Ahora puedes hacer que las barras del sistema sean parcialmente traslúcidas con temas nuevos, Theme.Holo.NoActionBar.TranslucentDecor y Theme.Holo.Light.NoActionBar.TranslucentDecor. Cuando habilitas las barras de sistema translúcidas, tu diseño llenará el área detrás de las barras del sistema, por lo que también debes habilitar fitsSystemWindows para la parte del diseño que no debería estar cubierta por las barras del sistema.

Si estás creando un tema personalizado, configura uno de estos temas como el tema principal o incluye las propiedades de estilo windowTranslucentNavigation y windowTranslucentStatus en tu tema.

Receptor de notificaciones mejorado

Android 4.3 agregó las API de NotificationListenerService, lo que permite que las apps reciban información sobre notificaciones nuevas a medida que el sistema las publica. En Android 4.4, los objetos de escucha de notificaciones pueden recuperar metadatos adicionales para la notificación y detalles completos de las acciones de la notificación:

El nuevo campo Notification.extras incluye un Bundle para entregar metadatos adicionales del compilador 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 desde el nuevo campo actions.

Duplicación de elementos de diseño para diseños RTL

En versiones anteriores de Android, si tu app incluye imágenes que deben invertir su orientación horizontal para diseños de derecha a izquierda, debes incluir la imagen duplicada en un directorio de recursos drawables-ldrtl/. Ahora, el sistema puede duplicar las imágenes automáticamente habilitando el atributo autoMirrored en un recurso de elemento de diseño o llamando a setAutoMirrored(). Cuando se habilita, Drawable se duplica automáticamente cuando la dirección del diseño es de derecha a izquierda.

Accesibilidad

La clase View ahora te permite declarar "regiones activas" para partes de tu IU que se actualizan dinámicamente con nuevo contenido de texto. 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 que muestra una notificación de “contraseña incorrecta” se debe marcar como región activa para que el lector de pantalla recite el mensaje cuando cambie.

Las apps que proporcionan un servicio de accesibilidad ahora también pueden mejorar sus capacidades con nuevas APIs que proporcionan información sobre las colecciones de vistas, como las vistas de lista o de cuadrícula, mediante AccessibilityNodeInfo.CollectionInfo y AccessibilityNodeInfo.CollectionItemInfo.

Permisos de la app

Los siguientes son permisos nuevos que tu app debe solicitar con la etiqueta <uses-permission> para usar determinadas APIs nuevas:

INSTALL_SHORTCUT
Permite que una app instale un acceso directo en el Selector
UNINSTALL_SHORTCUT
Permite que una aplicación desinstale un acceso directo en el Selector.
TRANSMIT_IR
Permite que una aplicación use el transmisor IR del dispositivo, si está 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 quieres acceder a regiones específicas de tu app del almacenamiento externo con métodos como getExternalFilesDir(). Sin embargo, los permisos siguen siendo obligatorios si quieres acceder a las regiones compartibles del almacenamiento externo, que proporciona getExternalStoragePublicDirectory().

Funciones del dispositivo

Las siguientes son 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 verificarlos durante el tiempo de ejecución:

FEATURE_CONSUMER_IR
El dispositivo es capaz de comunicarse con dispositivos infrarrojos de consumidor.
FEATURE_DEVICE_ADMIN
El dispositivo admite la aplicación de políticas de dispositivo a través de los administradores.
FEATURE_NFC_HOST_CARD_EMULATION
El dispositivo admite la emulación de tarjetas NFC basada en host.
FEATURE_SENSOR_STEP_COUNTER
El dispositivo incluye un contador de pasos de hardware.
FEATURE_SENSOR_STEP_DETECTOR
El dispositivo 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.