Android 4.3 API

Nivel de API: 18

Android 4.3 (JELLY_BEAN_MR2) es una actualización de la versión de Jelly Bean que ofrece funciones nuevas para usuarios y desarrolladores de apps. En este documento, se ofrece una introducción a las APIs nuevas más destacadas.

Como desarrollador de apps, debes descargar la imagen del sistema Android 4.3 y la plataforma de SDK de SDK Manager lo antes posible. Si no tienes un dispositivo con Android 4.3 en el que puedas probar la app, usa la imagen del sistema de Android 4.3 para probarla en el emulador de Android. Luego, compila tus apps con la plataforma Android 4.3 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.3, debes configurar targetSdkVersion como "18", instalarla en una imagen del sistema de Android 4.3, probarla y, luego, publicar una actualización con este cambio.

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

En la biblioteca de compatibilidad de Android, también hay disponibles varias APIs que te permiten implementar funciones nuevas en versiones anteriores de la plataforma.

Para obtener más información sobre cómo funcionan los niveles de API, consulta ¿Qué es el 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.3.

Si tu app usa intents implícitos...

Es posible que tu app se comporte de manera incorrecta en un entorno de perfil restringido.

Es posible que los usuarios de un entorno de perfil restringido no tengan disponibles todas las apps para Android estándar. Por ejemplo, un perfil restringido puede tener inhabilitados el navegador web y la app de cámara. Por lo tanto, tu app no debe hacer suposiciones sobre qué apps están disponibles, ya que si llamas a startActivity() sin verificar si una app está disponible para controlar el Intent, esta podría fallar en un perfil restringido.

Cuando usas un intent implícito, siempre debes verificar que una app esté disponible para controlar el intent llamando a resolveActivity() o queryIntentActivities(). Por ejemplo:

Kotlin

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

Java

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

Si tu app depende de cuentas...

Es posible que tu app se comporte de manera incorrecta en un entorno de perfil restringido.

Los usuarios de un entorno de perfil restringido no tienen acceso a las cuentas de usuario de forma predeterminada. Si tu app depende de un Account, es posible que falle o se comporte de forma inesperada cuando se use en un perfil restringido.

Si no quieres que los perfiles restringidos usen tu app por completo, ya que esta depende de información sensible de la cuenta, especifica el atributo android:requiredAccountType en el elemento <application> de tu manifiesto.

Si quieres permitir que los perfiles restringidos sigan usando tu app aunque no puedan crear sus propias cuentas, puedes inhabilitar las funciones de la app que requieren una cuenta o permitir que los perfiles restringidos accedan a las cuentas que creó el usuario principal. Para obtener más información, consulta la siguiente sección sobre Compatibilidad con cuentas en un perfil restringido.

Si tu app usa VideoView...

Es posible que el video parezca más pequeño en Android 4.3.

En versiones anteriores de Android, el widget VideoView calculó de forma incorrecta el valor "wrap_content" de layout_height y layout_width para que fuera igual que "match_parent". Por lo tanto, si bien es posible que el uso de "wrap_content" para la altura o el ancho haya proporcionado el diseño de video que deseas, puede generar un video mucho más pequeño en Android 4.3 y versiones posteriores. Para solucionar el problema, reemplaza "wrap_content" por "match_parent" y verifica que el video aparezca como se espera en Android 4.3, así como en versiones anteriores.

Perfiles restringidos

En las tablets Android, los usuarios ahora pueden crear perfiles restringidos basados en el usuario principal. Cuando los usuarios crean un perfil restringido, pueden habilitar restricciones como qué apps están disponibles para el perfil. Un nuevo conjunto de APIs en Android 4.3 también te permite crear parámetros de configuración de restricción detallados para las apps que desarrolles. Por ejemplo, con las APIs nuevas, puedes permitir que los usuarios controlen qué tipo de contenido está disponible dentro de tu app cuando se ejecuta en un entorno de perfil restringido.

La app de Configuración del sistema administra la IU para que los usuarios controlen las restricciones que compilaste. Para que el usuario vea la configuración de restricciones de tu app, debes declarar las restricciones que esta proporciona. Para ello, crea un BroadcastReceiver que reciba el intent ACTION_GET_RESTRICTION_ENTRIES. El sistema invoca este intent para consultar todas las apps sobre las restricciones disponibles y, luego, compila la IU para permitir que el usuario principal administre las restricciones de cada perfil restringido.

En el método onReceive() de tu BroadcastReceiver, debes crear un RestrictionEntry para cada restricción que proporcione tu app. Cada RestrictionEntry define un título de restricción, una descripción y uno de los siguientes tipos de datos:

  • TYPE_BOOLEAN para una restricción que es verdadera o falsa.
  • TYPE_CHOICE para una restricción que tiene varias opciones que son mutuamente excluyentes (opciones de botones de selección).
  • TYPE_MULTI_SELECT para una restricción que tiene varias opciones que no son mutuamente excluyentes (opciones de casillas de verificación).

Luego, coloca todos los objetos RestrictionEntry en un ArrayList y lo colocas en el resultado del receptor de emisión como el valor del EXTRA_RESTRICTIONS_LIST adicional.

El sistema crea la IU para las restricciones de tu app en la app de Configuración y guarda cada restricción con la clave única que proporcionaste para cada objeto RestrictionEntry. Cuando el usuario abre tu app, puedes llamar a getApplicationRestrictions() para consultar las restricciones actuales. Esto muestra una Bundle que contiene los pares clave-valor para cada restricción que definiste con los objetos RestrictionEntry.

Si deseas proporcionar restricciones más específicas que no puedan manejarse con valores booleanos, de opción única o de opción múltiple, puedes crear una actividad en la que el usuario pueda especificar las restricciones y permitir que los usuarios abran esa actividad desde la configuración de restricciones. En tu receptor de emisión, incluye el EXTRA_RESTRICTIONS_INTENT adicional en el Bundle de resultados. Ese elemento adicional debe especificar un Intent que indique la clase Activity que se iniciará (usa el método putParcelable() para pasar EXTRA_RESTRICTIONS_INTENT con el intent). Cuando el usuario principal ingresa a tu actividad para establecer restricciones personalizadas, esta debe mostrar un resultado que contenga los valores de restricción en un extra mediante la clave EXTRA_RESTRICTIONS_LIST o EXTRA_RESTRICTIONS_BUNDLE, según si especificas objetos RestrictionEntry o pares clave-valor, respectivamente.

Compatibilidad con cuentas en un perfil restringido

Las cuentas que se agreguen al usuario principal estarán disponibles para un perfil restringido, pero no se podrá acceder a las cuentas desde las APIs de AccountManager de forma predeterminada. Si intentas agregar una cuenta con AccountManager mientras tienes un perfil restringido, se mostrará un error. Debido a estas restricciones, tienes las siguientes tres opciones:

  • Permitir el acceso a las cuentas del propietario desde un perfil restringido.

    Para obtener acceso a una cuenta desde un perfil restringido, debes agregar el atributo android:restrictedAccountType a la etiqueta <application>:

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

    Precaución: Habilitar este atributo proporciona a tu app acceso a las cuentas de usuario principales desde los perfiles restringidos. Por lo tanto, debes permitir esto solo si la información que muestra tu app no revela información de identificación personal (PII) que se considera sensible. La configuración del sistema informará al usuario principal que tu app otorga perfiles restringidos a sus cuentas, por lo que debe quedar claro para el usuario que el acceso a la cuenta es importante para la funcionalidad de la app. Si es posible, también debes proporcionar controles de restricción adecuados para el usuario principal que definan cuánto acceso a la cuenta se permite en tu app.

  • Inhabilita ciertas funciones cuando no se puedan modificar las cuentas.

    Si quieres usar cuentas, pero no las necesitas para la funcionalidad principal de la app, puedes verificar la disponibilidad de las cuentas e inhabilitar funciones cuando no estén disponibles. Primero debes verificar si hay una cuenta existente disponible. De lo contrario, llama a getUserRestrictions() para consultar si es posible crear una cuenta nueva y verifica el DISALLOW_MODIFY_ACCOUNTS adicional en el resultado. Si es true, debes inhabilitar cualquier funcionalidad de tu app que requiera acceso a las cuentas. Por ejemplo:

    Kotlin

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

    Java

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

    Nota: En este caso, no debes declarar ningún atributo nuevo en tu archivo de manifiesto.

  • Inhabilita la app cuando no puedas acceder a las cuentas privadas.

    En cambio, si es importante que tu app no esté disponible para perfiles restringidos porque depende de información personal sensible de una cuenta (y porque los perfiles restringidos actualmente no pueden agregar cuentas nuevas), agrega el atributo android:requiredAccountType a la etiqueta <application>:

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

    Por ejemplo, la app de Gmail usa este atributo para inhabilitarse para los perfiles restringidos, porque el correo electrónico personal del propietario no debe estar disponible para los perfiles restringidos.

  • Redes inalámbricas y conectividad

    Bluetooth de bajo consumo (inteligente)

    Android ahora admite Bluetooth de bajo consumo (LE) con nuevas APIs en android.bluetooth. Con las nuevas APIs, puedes compilar apps para Android que se comuniquen con periféricos Bluetooth de bajo consumo, como monitores de frecuencia cardíaca y podómetros.

    Como Bluetooth LE es una función de hardware que no está disponible en todos los dispositivos que ejecutan Android, debes declarar en tu archivo de manifiesto un elemento <uses-feature> para "android.hardware.bluetooth_le":

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

    Si ya conoces las APIs de Bluetooth clásicas de Android, ten en cuenta que el uso de las APIs de Bluetooth LE tiene algunas diferencias. Lo más importante es que ahora hay una clase BluetoothManager que debes usar para algunas operaciones de alto nivel, como la adquisición de un BluetoothAdapter, la obtención de una lista de dispositivos conectados y la verificación del estado de un dispositivo. Por ejemplo, a continuación, se muestra cómo deberías obtener BluetoothAdapter:

    Kotlin

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

    Java

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

    Para descubrir los periféricos Bluetooth LE, llama a startLeScan() en BluetoothAdapter y pásale una implementación de la interfaz BluetoothAdapter.LeScanCallback. Cuando el adaptador de Bluetooth detecta un periférico Bluetooth LE, tu implementación de BluetoothAdapter.LeScanCallback recibe una llamada al método onLeScan(). Este método te proporciona un objeto BluetoothDevice que representa el dispositivo detectado, el valor RSSI del dispositivo y un array de bytes que contiene el registro de anuncio del dispositivo.

    Si solo deseas buscar tipos específicos de periféricos, puedes llamar a startLeScan() e incluir un array de objetos UUID que especifique los servicios GATT que admite tu app.

    Nota: Solo puedes buscar dispositivos Bluetooth LE o buscar dispositivos Bluetooth clásicos con las APIs anteriores. No puedes buscar dispositivos Bluetooth LE y Classic Bluetooth al mismo tiempo.

    Para conectarte a un periférico Bluetooth LE, llama a connectGatt() en el objeto BluetoothDevice correspondiente y pásale una implementación de BluetoothGattCallback. Tu implementación de BluetoothGattCallback recibe devoluciones de llamada sobre el estado de conectividad con el dispositivo y otros eventos. Durante la devolución de llamada onConnectionStateChange(), puedes comenzar a comunicarte con el dispositivo si el método pasa STATE_CONNECTED como el estado nuevo.

    El acceso a las funciones de Bluetooth en un dispositivo también requiere que la app solicite ciertos permisos de usuario de Bluetooth. Para obtener más información, consulta la guía de la API de Bluetooth de bajo consumo.

    Modo de solo búsqueda de Wi-Fi

    Al intentar identificar la ubicación del usuario, Android puede usar Wi-Fi para ayudar a determinar la ubicación escaneando los puntos de acceso cercanos. Sin embargo, los usuarios suelen mantener la conexión Wi-Fi desactivada para conservar la batería, lo que genera datos de ubicación menos precisos. Android ahora incluye un modo de solo búsqueda que permite que el Wi-Fi del dispositivo analice los puntos de acceso para ayudar a obtener la ubicación sin conectarse a un punto de acceso, lo que reduce en gran medida el uso de batería.

    Si deseas obtener la ubicación del usuario, pero la conexión Wi-Fi está desactivada, puedes solicitar al usuario que habilite el modo de solo búsqueda de Wi-Fi llamando a startActivity() con la acción ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE.

    Configuración de Wi-Fi

    Las nuevas APIs de WifiEnterpriseConfig permiten que los servicios empresariales automaticen la configuración de Wi-Fi para dispositivos administrados.

    Respuesta rápida para llamadas entrantes

    A partir de Android 4.0, una función llamada "Respuesta rápida" permite a los usuarios responder llamadas entrantes con un mensaje de texto inmediato sin necesidad de contestar la llamada ni desbloquear el dispositivo. Hasta ahora, la app predeterminada de mensajería siempre administraba estos mensajes rápidos. Ahora cualquier app puede declarar su capacidad de manejar estos mensajes creando un Service con un filtro de intents para ACTION_RESPOND_VIA_MESSAGE.

    Cuando el usuario responde a una llamada entrante con una respuesta rápida, la app de Teléfono envía el intent ACTION_RESPOND_VIA_MESSAGE con un URI que describe al destinatario (el emisor) y el EXTRA_TEXT adicional con el mensaje que el usuario desea enviar. Cuando tu servicio recibe el intent, debe entregar el mensaje y detenerse de inmediato (tu app no debe mostrar una actividad).

    Para recibir este intent, debes declarar el permiso SEND_RESPOND_VIA_MESSAGE.

    Multimedia

    Mejoras en MediaExtractor y MediaCodec

    Android ahora facilita la escritura de tus propios reproductores de transmisión dinámica adaptable a través de HTTP (DASH) de acuerdo con el estándar ISO/IEC 23009-1, mediante las APIs existentes en MediaCodec y MediaExtractor. Se actualizó el framework subyacente de estas APIs para admitir el análisis de archivos MP4 fragmentados, pero tu app sigue siendo responsable de analizar los metadatos de la MPD y de pasar las transmisiones individuales a MediaExtractor.

    Si deseas usar DASH con contenido encriptado, ten en cuenta que el método getSampleCryptoInfo() muestra los metadatos MediaCodec.CryptoInfo que describen la estructura de cada muestra de contenido multimedia encriptado. Además, se agregó el método getPsshInfo() a MediaExtractor para que puedas acceder a los metadatos de PSSH para tu medio de DASH. Este método muestra un mapa de objetos UUID a bytes, en el que UUID especifica el esquema criptográfico y los bytes son los datos específicos de ese esquema.

    DRM de contenido multimedia

    La nueva clase MediaDrm proporciona una solución modular para la administración de derechos digitales (DRM) con tu contenido multimedia mediante la separación de los problemas de DRM de la reproducción de contenido multimedia. Por ejemplo, esta separación de API te permite reproducir contenido encriptado con Widevine sin tener que usar el formato multimedia de Widevine. Esta solución DRM también es compatible con la encriptación común DASH, de modo que puedes usar una variedad de esquemas DRM con el contenido de transmisión.

    Puedes usar MediaDrm para obtener mensajes de solicitud de clave opacos y procesar mensajes de respuesta de clave del servidor para la adquisición y el aprovisionamiento de licencias. Tu app es responsable de manejar la comunicación de red con los servidores. La clase MediaDrm solo proporciona la capacidad de generar y procesar los mensajes.

    Las APIs de MediaDrm están diseñadas para usarse junto con las APIs de MediaCodec que se introdujeron en Android 4.1 (nivel de API 16), lo que incluye MediaCodec para codificar y decodificar tu contenido, MediaCrypto para administrar contenido encriptado y MediaExtractor para extraer y decodificar tu contenido.

    Primero debes construir los objetos MediaExtractor y MediaCodec. Luego, puedes acceder al UUID que identifica el esquema de DRM, por lo general, a partir de los metadatos del contenido, y usarlo para construir una instancia de un objeto MediaDrm con su constructor.

    Codificación de video desde una superficie

    Android 4.1 (nivel de API 16) agregó la clase MediaCodec para la codificación y decodificación de bajo nivel de contenido multimedia. Cuando se codifican videos, Android 4.1 requería que proporciones los medios con un array ByteBuffer, pero Android 4.3 ahora te permite usar un Surface como entrada para un codificador. Por ejemplo, esto te permite codificar la entrada de un archivo de video existente o el uso de marcos generados desde OpenGL ES.

    Para usar un Surface como entrada del codificador, primero llama a configure() para tu MediaCodec. Luego, llama a createInputSurface() para recibir el Surface en el que puedes transmitir tu contenido multimedia.

    Por ejemplo, puedes usar el objeto Surface determinado como ventana para un contexto de OpenGL si lo pasas a eglCreateWindowSurface(). Luego, mientras renderizas la superficie, llama a eglSwapBuffers() para pasar el fotograma a MediaCodec.

    Para comenzar a codificar, llama a start() en MediaCodec. Cuando termines, llama a signalEndOfInputStream() para finalizar la codificación y llama a release() en Surface.

    Combinación de medios

    La nueva clase MediaMuxer permite la multiplexación entre una transmisión de audio y una de video. Estas APIs sirven como contraparte de la clase MediaExtractor que se agregó en Android 4.2 para demultiplexación (demuxe) de contenido multimedia.

    Los formatos de salida admitidos se definen en MediaMuxer.OutputFormat. Actualmente, MP4 es el único formato de salida compatible y MediaMuxer admite solo una transmisión de audio o una de video a la vez.

    MediaMuxer se diseñó principalmente para funcionar con MediaCodec, de modo que puedas realizar el procesamiento de video a través de MediaCodec y, luego, guardar el resultado en un archivo MP4 a través de MediaMuxer. También puedes usar MediaMuxer junto con MediaExtractor para editar contenido multimedia sin necesidad de codificar o decodificar.

    Progreso de reproducción y arrastre de RemoteControlClient

    En Android 4.0 (nivel de API 14), se agregó RemoteControlClient para habilitar los controles de reproducción de contenido multimedia desde clientes de control remoto, como los controles disponibles en la pantalla de bloqueo. Android 4.3 ahora permite que esos controladores muestren la posición de reproducción y los controles para arrastrarla. Si habilitaste el control remoto para tu app de música con las APIs de RemoteControlClient, puedes permitir la limpieza de reproducción implementando dos interfaces nuevas.

    Primero, debes habilitar la marca FLAG_KEY_MEDIA_POSITION_UPDATE. Para ello, pásala a setTransportControlsFlags().

    A continuación, implementa las siguientes dos interfaces nuevas:

    RemoteControlClient.OnGetPlaybackPositionListener
    Esto incluye la devolución de llamada onGetPlaybackPosition(), que solicita la posición actual del contenido multimedia cuando el control remoto necesita actualizar el progreso en la IU.
    RemoteControlClient.OnPlaybackPositionUpdateListener
    Esto incluye el onPlaybackPositionUpdate() de devolución de llamada, que le indica a tu app el nuevo código de tiempo para el contenido multimedia cuando el usuario arrastra la reproducción con la IU del control remoto.

    Una vez que actualices la reproducción con la nueva posición, llama a setPlaybackState() para indicar el nuevo estado, posición y velocidad de reproducción.

    Con estas interfaces definidas, puedes configurarlas para tu RemoteControlClient llamando a setOnGetPlaybackPositionListener() y setPlaybackPositionUpdateListener(), respectivamente.

    Gráficos

    Compatibilidad con OpenGL ES 3.0

    Android 4.3 agrega interfaces Java y compatibilidad nativa con OpenGL ES 3.0. La nueva funcionalidad clave proporcionada en OpenGL ES 3.0 incluye lo siguiente:

    • Aceleración de efectos visuales avanzados
    • Compresión de texturas ETC2/EAC de alta calidad como función estándar
    • Una nueva versión del lenguaje de sombreado GLSL ES compatible con números enteros y punto flotante de 32 bits
    • Renderización avanzada de texturas
    • Estandarización más amplia del tamaño de las texturas y los formatos de búfer de renderización

    La interfaz de Java para OpenGL ES 3.0 en Android se proporciona con GLES30. Cuando uses OpenGL ES 3.0, asegúrate de declararlo en tu archivo de manifiesto con la etiqueta <uses-feature> y el atributo android:glEsVersion. Por ejemplo:

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

    Recuerda especificar el contexto de OpenGL ES llamando a setEGLContextClientVersion() y pasando 3 como la versión.

    Para obtener más información sobre el uso de OpenGL ES, incluida la manera de comprobar la versión de OpenGL ES compatible con el dispositivo durante el tiempo de ejecución, consulta la guía de la API de OpenGL ES.

    Asignación mipmaps para elementos de diseño

    Usar un mipmap como fuente para tu mapa de bits o elemento de diseño es una forma sencilla de proporcionar una imagen de calidad y varias escalas de imagen, lo que puede resultar particularmente útil si esperas que la imagen se ajuste durante una animación.

    Android 4.2 (nivel de API 17) agregó compatibilidad con mipmaps en la clase Bitmap: Android intercambia las imágenes mip en tu Bitmap cuando proporcionaste una fuente de mipmap y habilitaste setHasMipMap(). Ahora, en Android 4.3, también puedes habilitar mipmaps para un objeto BitmapDrawable proporcionando un recurso de mipmap y configurando el atributo android:mipMap en un archivo de recursos de mapa de bits o llamando a hasMipMap().

    Interfaz de usuario

    Superposición de objetos View

    La nueva clase ViewOverlay proporciona una capa transparente sobre un View, en la que puedes agregar contenido visual y no afecta la jerarquía de diseño. Puedes obtener un ViewOverlay para cualquier View llamando a getOverlay(). La superposición siempre tiene el mismo tamaño y posición que su vista de host (la vista desde la que se creó), lo que te permite agregar contenido que aparece frente a ella, pero que no puede extender los límites de esa vista.

    El uso de un objeto ViewOverlay resulta particularmente útil cuando deseas crear animaciones, como deslizar una vista fuera de su contenedor o mover elementos por la pantalla sin afectar la jerarquía de vistas. Sin embargo, debido a que el área utilizable de una superposición está restringida a la misma área que su vista de host, si deseas animar una vista que se mueva fuera de su posición en el diseño, debes usar una superposición de una vista superior que tenga los límites de diseño deseados.

    Cuando creas una superposición para una vista de widget, como Button, puedes llamar a add(Drawable) para agregar objetos Drawable a la superposición. Si llamas a getOverlay() para una vista de diseño, como RelativeLayout, el objeto que se muestra es ViewGroupOverlay. La clase ViewGroupOverlay es una subclase de ViewOverlay que también te permite agregar objetos View llamando a add(View).

    Nota: Todos los elementos de diseño y las vistas que agregas a una superposición son solo visuales. No pueden recibir eventos de enfoque ni de entrada.

    Por ejemplo, el siguiente código anima una vista que se desliza hacia la derecha. Para ello, se coloca la vista en la superposición de la vista superior y, luego, se realiza una animación de traducción en esa vista:

    Kotlin

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

    Java

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

    Diseño de límites ópticos

    Para las vistas que contienen imágenes de fondo nine-patch, ahora puedes especificar que se deben alinear con vistas vecinas en función de los límites "ópticos" de la imagen de fondo en lugar de los límites de "clip" de la vista.

    Por ejemplo, en las figuras 1 y 2, se muestra el mismo diseño, pero en la versión de la figura 1 se usan límites de recorte (el comportamiento predeterminado), mientras que en la figura 2 se usan límites ópticos. Debido a que las imágenes de nueve parches que se usan para el botón y el marco de fotos incluyen padding alrededor de los bordes, no parecen alinearse entre sí ni con el texto cuando se usan límites de recorte.

    Nota: La captura de pantalla de las Figuras 1 y 2 tiene habilitada la configuración para desarrolladores "Mostrar límites de diseño". En cada vista, las líneas rojas indican los límites ópticos, las líneas azules indican los límites de recorte y el rosa indica los márgenes.

    Figura 1: Diseño con límites de recorte (predeterminado)

    Figura 2: Diseño con límites ópticos

    Para alinear las vistas en función de sus límites ópticos, configura el atributo android:layoutMode como "opticalBounds" en uno de los diseños superiores. Por ejemplo:

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

    Figura 3: Vista con zoom del nueve parches del botón Holo con límites ópticos.

    Para que esto funcione, las imágenes nine-patch aplicadas al fondo de tus vistas deben especificar los límites ópticos mediante líneas rojas a lo largo de la parte inferior y derecha del archivo nine-patch (como se muestra en la figura 3). Las líneas rojas indican la región que debe quitarse de los límites de recorte, dejando los límites ópticos de la imagen.

    Cuando habilitas los límites ópticos para una ViewGroup en tu diseño, todas las vistas descendentes heredan el modo de diseño de límites ópticos, a menos que los anules para un grupo estableciendo android:layoutMode en "clipBounds". Todos los elementos de diseño también respetan los límites ópticos de sus vistas secundarias y adaptan sus propios límites en función de los límites ópticos de las vistas dentro de ellas. Sin embargo, por el momento, los elementos de diseño (subclases de ViewGroup) no admiten límites ópticos para imágenes nine-patch aplicados a su propio fondo.

    Si creas una vista personalizada mediante la subclasificación de View, ViewGroup o cualquier subclase de estos, tu vista heredará estos comportamientos de límite óptico.

    Nota: Todos los widgets compatibles con el tema Holo se actualizaron con límites ópticos, incluidos Button, Spinner, EditText y otros. Por lo tanto, puedes beneficiarte de inmediato si configuras el atributo android:layoutMode como "opticalBounds" si tu app aplica un tema Holo (Theme.Holo, Theme.Holo.Light, etc.).

    Para especificar límites ópticos para tus propias imágenes de nine-patch con la herramienta Draw 9-patch, mantén presionada la tecla Control cuando hagas clic en los píxeles del borde.

    Animación para valores Rect

    Ahora puedes animar entre dos valores de Rect con el nuevo RectEvaluator Esta nueva clase es una implementación de TypeEvaluator que puedes pasar a ValueAnimator.setEvaluator().

    Adjuntar una ventana y enfocar el objeto de escucha

    Anteriormente, si querías escuchar cuando tu vista se adjuntaba o desconectaba a la ventana, o cuando cambiaba su enfoque, necesitabas anular la clase View para implementar onAttachedToWindow() y onDetachedFromWindow(), o onWindowFocusChanged(), respectivamente.

    Ahora, para recibir eventos de adjuntar y desconectar, puedes implementar ViewTreeObserver.OnWindowAttachListener y configurarlo en una vista con addOnWindowAttachListener(). Para recibir eventos de enfoque, puedes implementar ViewTreeObserver.OnWindowFocusChangeListener y configurarlo en una vista con addOnWindowFocusChangeListener().

    Compatibilidad con el sobrebarrido de TV

    Para asegurarte de que tu app ocupe toda la pantalla de cada televisión, ahora puedes habilitar el sobrebarrido en el diseño de tu app. El modo de sobrebarrido se determina con la marca FLAG_LAYOUT_IN_OVERSCAN, que puedes habilitar con temas de plataforma, como Theme_DeviceDefault_NoActionBar_Overscan o si habilitas el estilo windowOverscan en un tema personalizado.

    Orientación de la pantalla

    El atributo screenOrientation de la etiqueta <activity> ahora admite valores adicionales para respetar la preferencia del usuario por la rotación automática:

    "userLandscape"
    Se comporta de la misma manera que "sensorLandscape", excepto que si el usuario inhabilita la opción de girar automáticamente, se bloqueará en la orientación horizontal normal y no cambiará.
    "userPortrait"
    Se comporta de la misma manera que "sensorPortrait", excepto que si el usuario inhabilita la opción de girar automáticamente, se bloqueará en la orientación vertical normal y no cambiará.
    "fullUser"
    Se comporta de la misma manera que "fullSensor" y permite la rotación en las cuatro direcciones, excepto si el usuario inhabilita la opción de rotación automática, este bloquea la orientación que prefiere.

    Además, ahora también puedes declarar "locked" para bloquear la orientación de tu app en la orientación actual de la pantalla.

    Animaciones de rotación

    El nuevo campo rotationAnimation de WindowManager te permite seleccionar entre una de las tres animaciones que quieras usar cuando el sistema cambie de orientación de pantalla. Estas son las tres animaciones:

    Nota: Estas animaciones solo están disponibles si configuraste tu actividad para que use el modo de "pantalla completa", el cual puedes habilitar con temas como Theme.Holo.NoActionBar.Fullscreen.

    Por ejemplo, aquí te mostramos cómo puedes habilitar la animación de "fundido cruzado":

    Kotlin

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

    Java

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

    Entrada del usuario

    Tipos de sensores nuevos

    El nuevo sensor TYPE_GAME_ROTATION_VECTOR te permite detectar las rotaciones del dispositivo sin preocuparte por las interferencias magnéticas. A diferencia del sensor TYPE_ROTATION_VECTOR, el TYPE_GAME_ROTATION_VECTOR no se basa en el norte magnético.

    Los nuevos sensores TYPE_GYROSCOPE_UNCALIBRATED y TYPE_MAGNETIC_FIELD_UNCALIBRATED proporcionan datos de sensores sin procesar sin considerar las estimaciones de sesgo. Es decir, los sensores TYPE_GYROSCOPE y TYPE_MAGNETIC_FIELD existentes proporcionan datos de sensores que tienen en cuenta el sesgo estimado de la deriva giratoria y del hierro resistente en el dispositivo, respectivamente. En cambio, las nuevas versiones "sin calibrar" de estos sensores proporcionan los datos sin procesar del sensor y ofrecen los valores de sesgo estimados por separado. Estos sensores te permiten proporcionar tu propia calibración personalizada para los datos del sensor mediante la mejora del sesgo estimado con datos externos.

    Notificaciones del objeto de escucha

    En Android 4.3, se agrega una nueva clase de servicio, NotificationListenerService, que permite que tu app reciba información sobre notificaciones nuevas a medida que el sistema las publica.

    Si tu app actualmente usa las APIs del servicio de accesibilidad para acceder a las notificaciones del sistema, debes actualizarla para que use estas APIs.

    Proveedor de contactos

    Cómo realizar búsquedas de "contactables"

    La nueva consulta del proveedor de contactos, Contactables.CONTENT_URI, proporciona una forma eficiente de obtener un Cursor que contiene todas las direcciones de correo electrónico y los números de teléfono de todos los contactos que coinciden con la consulta especificada.

    Consulta deltas de contactos

    Se agregaron nuevas API al Proveedor de contactos que te permiten consultar de manera eficiente los cambios recientes en los datos de los contactos. Anteriormente, tu app podía recibir notificaciones cuando se modificaba algo en los datos de los contactos, pero no sabrías exactamente qué cambió y debías recuperar todos los contactos y, luego, iterarlos para descubrir el cambio.

    Para realizar un seguimiento de los cambios de inserciones y actualizaciones, ahora puedes incluir el parámetro CONTACT_LAST_UPDATED_TIMESTAMP con tu selección para consultar solo los contactos que cambiaron desde la última vez que consultaste al proveedor.

    Para hacer un seguimiento de los contactos borrados, la nueva tabla ContactsContract.DeletedContacts proporciona un registro de los contactos que se borraron (pero cada contacto borrado se conserva en esta tabla por un tiempo limitado). Al igual que con CONTACT_LAST_UPDATED_TIMESTAMP, puedes usar el nuevo parámetro de selección, CONTACT_DELETED_TIMESTAMP, para comprobar qué contactos se borraron desde la última vez que consultaste al proveedor. La tabla también contiene la constante DAYS_KEPT_MILLISECONDS que contiene la cantidad de días (en milisegundos) que se conservará el registro.

    Además, el Proveedor de contactos ahora transmite la acción CONTACTS_DATABASE_CREATED cuando el usuario borra el almacenamiento de contactos a través del menú de configuración del sistema y vuelve a crear de manera efectiva la base de datos del Proveedor de contactos. Su objetivo es indicar a las apps que deben descartar toda la información de contacto que almacenaron y volver a cargarla con una consulta nueva.

    Para ver un código de muestra que usa estas APIs para verificar si hay cambios en los contactos, busca el ejemplo de ApiDemos disponible en la descarga de Muestras del SDK.

    Localización

    Compatibilidad mejorada con texto bidireccional

    Las versiones anteriores de Android admiten diseños y idiomas de derecha a izquierda (RTL), pero a veces no manejan correctamente texto en direcciones mixtas. Por lo tanto, Android 4.3 agrega las APIs de BidiFormatter que te ayudan a dar formato correctamente al texto con contenido en dirección opuesta sin generar elementos confusos.

    Por ejemplo, cuando deseas crear una oración con una variable de cadena, como "¿Quisiste decir 15 Bay Street, Laurel, CA?", en general, pasas un recurso de cadenas localizado y la variable a String.format():

    Kotlin

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

    Java

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

    Sin embargo, si la configuración regional es hebreo, entonces la cadena con formato aparece así:

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

    La respuesta es incorrecta, ya que el número “15” debe quedar a la izquierda de “Bay Street”. La solución es usar BidiFormatter y su método unicodeWrap(). Por ejemplo, el código anterior se convierte en lo siguiente:

    Kotlin

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

    Java

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

    De forma predeterminada, unicodeWrap() usa la primera heurística de estimación de direccionalidad sólida, que puede fallar si la primera señal para la dirección de texto no representa la dirección adecuada para el contenido en su conjunto. Si es necesario, puedes especificar una heurística diferente pasando una de las constantes TextDirectionHeuristic de TextDirectionHeuristics a unicodeWrap().

    Nota: Estas nuevas APIs también están disponibles para versiones anteriores de Android a través de la Biblioteca de compatibilidad de Android, con la clase BidiFormatter y las APIs relacionadas.

    Servicios de accesibilidad

    Cómo controlar eventos clave

    Un AccessibilityService ahora puede recibir una devolución de llamada para eventos de entrada de teclas con el método de devolución de llamada onKeyEvent(). Esto permite que tu servicio de accesibilidad controle la entrada para dispositivos de entrada basados en teclas, como un teclado, y traduzca esos eventos en acciones especiales que antes solo podían haber sido posibles con la entrada táctil o el mando de dirección del dispositivo.

    Seleccionar texto y copiar y pegar

    El AccessibilityNodeInfo ahora proporciona APIs que permiten que un AccessibilityService seleccione, corte, copie y pegue texto en un nodo.

    Para especificar la selección del texto que se cortará o copiará, tu servicio de accesibilidad puede usar la nueva acción, ACTION_SET_SELECTION, y pasarle la posición de inicio y finalización de la selección con ACTION_ARGUMENT_SELECTION_START_INT y ACTION_ARGUMENT_SELECTION_END_INT. Como alternativa, puedes seleccionar texto. Para ello, manipula la posición del cursor con la acción existente, ACTION_NEXT_AT_MOVEMENT_GRANULARITY (antes solo para mover la posición del cursor) y agrega el argumento ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN.

    Luego, puedes cortar o copiar con ACTION_CUT o ACTION_COPY y, luego, pegarlo con ACTION_PASTE.

    Nota: Estas nuevas APIs también están disponibles para versiones anteriores de Android a través de la Biblioteca de compatibilidad de Android, con la clase AccessibilityNodeInfoCompat.

    Declara las funciones de accesibilidad

    A partir de Android 4.3, un servicio de accesibilidad debe declarar las capacidades de accesibilidad en su archivo de metadatos para usar ciertas funciones de accesibilidad. Si la función no se solicita en el archivo de metadatos, la función será una no-op. Para declarar las capacidades de accesibilidad de tu servicio, debes usar atributos XML que correspondan a las diversas constantes de "capacidad" de la clase AccessibilityServiceInfo.

    Por ejemplo, si un servicio no solicita la función flagRequestFilterKeyEvents, no recibirá eventos de tecla.

    Pruebas y depuración

    Pruebas automatizadas de IU

    La nueva clase UiAutomation proporciona APIs que te permiten simular acciones del usuario para la automatización de pruebas. Mediante el uso de las APIs de AccessibilityService de la plataforma, las APIs de UiAutomation te permiten inspeccionar el contenido de la pantalla y, luego, insertar eventos táctiles y de teclado arbitrarios.

    Para obtener una instancia de UiAutomation, llama a Instrumentation.getUiAutomation(). Para que esto funcione, debes proporcionar a la opción -w el comando instrument cuando ejecutes tu InstrumentationTestCase desde adb shell.

    Con la instancia de UiAutomation, puedes ejecutar eventos arbitrarios para probar tu app. Para ello, llama a executeAndWaitForEvent() y pásale un Runnable para que realice, un tiempo de espera de la operación y una implementación de la interfaz UiAutomation.AccessibilityEventFilter. Dentro de tu implementación de UiAutomation.AccessibilityEventFilter, recibirás una llamada que te permitirá filtrar los eventos que te interesen y determinar el éxito o el fracaso de un caso de prueba determinado.

    Para observar todos los eventos durante una prueba, crea una implementación de UiAutomation.OnAccessibilityEventListener y pásala a setOnAccessibilityEventListener(). Luego, la interfaz del objeto de escucha recibe una llamada a onAccessibilityEvent() cada vez que ocurre un evento y recibe un objeto AccessibilityEvent que describe el evento.

    Existe una variedad de otras operaciones que las APIs de UiAutomation exponen a un nivel muy bajo para fomentar el desarrollo de herramientas de prueba de IU, como uiautomator. Por ejemplo, UiAutomation también puede hacer lo siguiente:

    • Cómo inyectar eventos de entrada
    • Cómo cambiar la orientación de la pantalla
    • Cómo tomar capturas de pantalla

    Y, lo más importante, para las herramientas de prueba de IU, las APIs de UiAutomation funcionan entre los límites de la aplicación, a diferencia de las de Instrumentation.

    Eventos de Systrace para apps

    Android 4.3 agrega la clase Trace con dos métodos estáticos, beginSection() y endSection(), que te permiten definir bloques de código para incluir con el informe de Systrace. Cuando se crean secciones de código rastreable en tu app, los registros de Systrace te proporcionan un análisis mucho más detallado de dónde se produce la demora dentro de tu app.

    Si deseas obtener información sobre el uso de la herramienta Systrace, consulta el artículo Análisis de Display y rendimiento con Systrace.

    Seguridad

    Tienda de claves de Android para claves privadas de apps

    Android ahora ofrece un proveedor de seguridad de Java personalizado en la instalación de KeyStore, llamado Android Key Store, que te permite generar y guardar claves privadas que solo tu app puede ver y usar. Para cargar Android Key Store, pasa "AndroidKeyStore" a KeyStore.getInstance().

    Para administrar las credenciales privadas de tu app en Android Key Store, genera una clave nueva con KeyPairGenerator y KeyPairGeneratorSpec. Primero, obtén una instancia de KeyPairGenerator llamando a getInstance(). Luego, llama a initialize() y pásale una instancia de KeyPairGeneratorSpec, que puedes obtener usando KeyPairGeneratorSpec.Builder. Por último, obtén tu KeyPair llamando a generateKeyPair().

    Almacenamiento de credenciales de hardware

    Android ahora también admite el almacenamiento respaldado por hardware para tus credenciales de KeyChain, lo que brinda más seguridad, ya que las claves no están disponibles para la extracción. Es decir, una vez que las claves están en un almacén de claves con copia de seguridad en hardware (Secure Element, TPM o TrustZone), se pueden usar para operaciones criptográficas, pero el material de la clave privada no se puede exportar. Incluso el kernel del SO no puede acceder a este material de claves. Si bien no todos los dispositivos Android admiten almacenamiento en hardware, puedes verificar si el almacenamiento de la copia de seguridad en hardware está disponible durante el tiempo de ejecución llamando a KeyChain.IsBoundKeyAlgorithm().

    Declaraciones de manifiesto

    Funciones requeridas declarables

    Los siguientes valores ahora son compatibles con el elemento <uses-feature>, de modo que puedas asegurarte de que tu app solo se instale en dispositivos que proporcionen las funciones que necesita.

    FEATURE_APP_WIDGETS
    Declara que tu app proporciona un widget de la app y debe instalarse solo en dispositivos con una pantalla principal o una ubicación similar donde los usuarios puedan incorporar widgets de apps. Ejemplo:
    <uses-feature android:name="android.software.app_widgets" android:required="true" />
    
    FEATURE_HOME_SCREEN
    Declara que tu app funciona como reemplazo de la pantalla principal y solo debe instalarse en dispositivos que admiten apps de terceros. Ejemplo:
    <uses-feature android:name="android.software.home_screen" android:required="true" />
    
    FEATURE_INPUT_METHODS
    Declara que tu app proporciona un método de entrada personalizado (un teclado compilado con InputMethodService) y debe instalarse solo en dispositivos que admiten métodos de entrada de terceros. Ejemplo:
    <uses-feature android:name="android.software.input_methods" android:required="true" />
    
    FEATURE_BLUETOOTH_LE
    Declara que tu app usa APIs de Bluetooth de bajo consumo y que solo debe instalarse en dispositivos que pueden comunicarse con otros dispositivos a través de Bluetooth de bajo consumo. Ejemplo:
    <uses-feature android:name="android.software.bluetooth_le" android:required="true" />
    

    Permisos del usuario

    Los siguientes valores ahora son compatibles con <uses-permission> para declarar los permisos que requiere tu app para acceder a ciertas APIs.

    BIND_NOTIFICATION_LISTENER_SERVICE
    Obligatorio para usar las nuevas APIs de NotificationListenerService.
    SEND_RESPOND_VIA_MESSAGE
    Obligatorio para recibir el intent ACTION_RESPOND_VIA_MESSAGE.

    Para obtener una vista detallada de todos los cambios de la API en Android 4.3, consulta el Informe de diferencias de las APIs.