Cambios en el comportamiento: apps orientadas a Android 15 o versiones posteriores

Al igual que las versiones anteriores, Android 15 incluye cambios de comportamiento que podrían afectar tu app. Los siguientes cambios se aplican exclusivamente a las apps orientadas a Android 15 o versiones posteriores. Si tu app está orientada a Android 15 o versiones posteriores, debes modificarla para que admita estos comportamientos correctamente, cuando corresponda.

Asegúrate de revisar también la lista de cambios de comportamiento que afectan a todas las apps que se ejecutan en Android 15, independientemente de targetSdkVersion de la app.

Funcionalidad principal

Android 15 modifica o amplía varias funciones principales del sistema Android.

Cambios en los servicios en primer plano

Realizamos los siguientes cambios en los servicios en primer plano con Android 15.

Comportamiento de tiempo de espera del servicio en primer plano de la sincronización de datos

Android 15 introduce un nuevo comportamiento de tiempo de espera en dataSync para las apps orientadas a Android 15 (nivel de API 35) o versiones posteriores. Este comportamiento también se aplica al nuevo tipo de servicio en primer plano mediaProcessing.

El sistema permite que los servicios dataSync de una app se ejecuten durante un total de 6 horas en un período de 24 horas, después de lo cual el sistema llama al método Service.onTimeout(int, int) del servicio en ejecución (presentado en Android 15). En este momento, el servicio tiene unos segundos para llamar a Service.stopSelf(). Cuando se llama a Service.onTimeout(), el servicio ya no se considera un servicio en primer plano. Si el servicio no llama a Service.stopSelf(), el sistema arroja una excepción interna. La excepción se registra en Logcat con el siguiente mensaje:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type dataSync did not stop within its timeout: [component name]"

Para evitar problemas con este cambio de comportamiento, puedes realizar una o más de las siguientes acciones:

  1. Haz que tu servicio implemente el nuevo método Service.onTimeout(int, int). Cuando tu app reciba la devolución de llamada, asegúrate de llamar a stopSelf() en unos pocos segundos. (Si no detienes la app de inmediato, el sistema genera una falla).
  2. Asegúrate de que los servicios dataSync de tu app no se ejecuten durante más de un total de 6 horas en cualquier período de 24 horas (a menos que el usuario interactúe con la app y restablezca el temporizador).
  3. Solo inicia los servicios en primer plano de dataSync como resultado de la interacción directa del usuario. Dado que tu app está en primer plano cuando se inicia el servicio, el servicio tiene seis horas completas después de que la app pasa a segundo plano.
  4. En lugar de usar un servicio en primer plano de dataSync, usa una API alternativa.

Si los servicios en primer plano dataSync de tu app se ejecutaron durante 6 horas en las últimas 24, no puedes iniciar otro servicio en primer plano dataSync a menos que el usuario haya llevado tu app al primer plano (lo que restablece el temporizador). Si intentas iniciar otro servicio en primer plano dataSync, el sistema arrojará una ForegroundServiceStartNotAllowedException con un mensaje de error como "El límite de tiempo ya se agotó para el tipo de servicio en primer plano dataSync".

Prueba

Para probar el comportamiento de tu app, puedes habilitar los tiempos de espera de sincronización de datos, incluso si tu app no se orienta a Android 15 (siempre que la app se ejecute en un dispositivo Android 15). Para habilitar los tiempos de espera, ejecuta el siguiente comando adb:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

También puedes ajustar el tiempo de espera para facilitar la prueba del comportamiento de tu app cuando se alcanza el límite. Para establecer un nuevo período de tiempo de espera, ejecuta el siguiente comando adb:

adb shell device_config put activity_manager data_sync_fgs_timeout_duration duration-in-milliseconds

Nuevo tipo de servicio en primer plano de procesamiento de contenido multimedia

Android 15 introduces a new foreground service type, mediaProcessing. This service type is appropriate for operations like transcoding media files. For example, a media app might download an audio file and need to convert it to a different format before playing it. You can use a mediaProcessing foreground service to make sure the conversion continues even while the app is in the background.

The system permits an app's mediaProcessing services to run for a total of 6 hours in a 24-hour period, after which the system calls the running service's Service.onTimeout(int, int) method (introduced in Android 15). At this time, the service has a few seconds to call Service.stopSelf(). If the service does not call Service.stopSelf(), the system throws an internal exception. The exception is logged in Logcat with the following message:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type mediaProcessing did not stop within its timeout: [component name]"

To avoid having the exception, you can do one of the following:

  1. Have your service implement the new Service.onTimeout(int, int) method. When your app receives the callback, make sure to call stopSelf() within a few seconds. (If you don't stop the app right away, the system generates a failure.)
  2. Make sure your app's mediaProcessing services don't run for more than a total of 6 hours in any 24-hour period (unless the user interacts with the app, resetting the timer).
  3. Only start mediaProcessing foreground services as a result of direct user interaction; since your app is in the foreground when the service starts, your service has the full six hours after the app goes to the background.
  4. Instead of using a mediaProcessing foreground service, use an alternative API, like WorkManager.

If your app's mediaProcessing foreground services have run for 6 hours in the last 24, you cannot start another mediaProcessing foreground service unless the user has brought your app to the foreground (which resets the timer). If you try to start another mediaProcessing foreground service, the system throws ForegroundServiceStartNotAllowedException with an error message like "Time limit already exhausted for foreground service type mediaProcessing".

For more information about the mediaProcessing service type, see Changes to foreground service types for Android 15: Media processing.

Testing

To test your app's behavior, you can enable media processing timeouts even if your app is not targeting Android 15 (as long as the app is running on an Android 15 device). To enable timeouts, run the following adb command:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

You can also adjust the timeout period, to make it easier to test how your app behaves when the limit is reached. To set a new timeout period, run the following adb command:

adb shell device_config put activity_manager media_processing_fgs_timeout_duration duration-in-milliseconds

Restricciones en los receptores de emisión de BOOT_COMPLETED que lanzan servicios en primer plano

Hay nuevas restricciones para los receptores de emisión de BOOT_COMPLETED que se lanzan servicios en primer plano. Los receptores BOOT_COMPLETED no pueden iniciar los siguientes tipos de servicios en primer plano:

Si un receptor BOOT_COMPLETED intenta iniciar cualquiera de esos tipos de primer plano. servicios, el sistema arroja una ForegroundServiceStartNotAllowedException.

Prueba

Para probar el comportamiento de tu app, puedes habilitar estas nuevas restricciones, incluso si tu app no está segmentada para Android 15 (siempre que la app se ejecute en un dispositivo con Android 15). Ejecuta el siguiente comando adb:

adb shell am compat enable FGS_BOOT_COMPLETED_RESTRICTIONS your-package-name

Para enviar una transmisión de BOOT_COMPLETED sin reiniciar el dispositivo, haz lo siguiente: Ejecuta el siguiente comando adb:

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED your-package-name

Restricciones para iniciar servicios en primer plano mientras una app tiene el permiso SYSTEM_ALERT_WINDOW

Anteriormente, si una app tenía el permiso SYSTEM_ALERT_WINDOW, podía iniciar un servicio en primer plano, incluso si la app estaba en segundo plano (como se explica en exenciones de las restricciones de inicio en segundo plano).

Si una app está orientada a Android 15, esta exención ahora es más limitada. Ahora, la app debe tener el permiso SYSTEM_ALERT_WINDOW y también tener una ventana superpuesta visible. Es decir, la app primero debe iniciar una ventana TYPE_APPLICATION_OVERLAY y la ventana debe ser visible antes de iniciar un servicio en primer plano.

Si tu app intenta iniciar un servicio en primer plano desde segundo plano sin cumplir con estos nuevos requisitos (y no tiene otra exención), el sistema arroja ForegroundServiceStartNotAllowedException.

Si tu app declara el permiso SYSTEM_ALERT_WINDOW y, luego, inicia servicios en primer plano desde el segundo plano, es posible que se vea afectada por este cambio. Si tu app obtiene una ForegroundServiceStartNotAllowedException, verifica el orden de operaciones de tu app y asegúrate de que ya tenga una ventana de superposición activa antes de intentar iniciar un servicio en primer plano desde el segundo plano. Puedes verificar si tu ventana de superposición es visible actualmente llamando a View.getWindowVisibility() o puedes anular View.onWindowVisibilityChanged() para recibir notificaciones cada vez que cambie la visibilidad.

Prueba

Para probar el comportamiento de tu app, puedes habilitar estas nuevas restricciones, incluso si tu app no está segmentada para Android 15 (siempre que la app se ejecute en un dispositivo con Android 15). Para habilitar estas nuevas restricciones para iniciar servicios en primer plano desde segundo plano, ejecuta el siguiente comando adb:

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

Se modificaron los momentos en que las apps pueden modificar el estado global del modo No interrumpir.

Las apps orientadas a Android 15 ya no pueden cambiar el estado global ni la política de No interrumpir (No interrumpir) en un dispositivo (ya sea modificando la configuración del usuario o desactivando el modo No interrumpir). En cambio, las apps deben aportar un AutomaticZenRule, que el sistema combina en una política global con el esquema existente más restrictivo de la política. Las llamadas a las APIs existentes que anteriormente afectaron el estado global (setInterruptionFilter, setNotificationPolicy) generan la creación o actualización de un AutomaticZenRule implícito, que se activa y desactiva según el ciclo de llamadas de esas llamadas a la API.

Ten en cuenta que este cambio solo afecta el comportamiento observable si la app llama a setInterruptionFilter(INTERRUPTION_FILTER_ALL) y espera que esa llamada desactive una AutomaticZenRule que sus propietarios hayan activado.

Cambios en la API de OpenJDK

Android 15 continúa la tarea de actualizar las bibliotecas principales de Android para alinearlas con las funciones de las versiones más recientes de LTS de OpenJDK.

Algunos de estos cambios pueden afectar la compatibilidad de las apps orientadas a Android 15 (nivel de API 35):

  • Cambios en las APIs de formato de cadenas: La validación del índice de argumentos, las marcas, la precisión y el ancho ahora es más estricta cuando se usan las siguientes APIs de String.format() y Formatter.format():

    Por ejemplo, se genera la siguiente excepción cuando se usa un índice de argumento de 0 (%0 en la cadena de formato):

    IllegalFormatArgumentIndexException: Illegal format argument index = 0
    

    En este caso, el problema se puede solucionar con un índice de argumento de 1 (%1 en la cadena de formato).

  • Cambios en el tipo de componente de Arrays.asList(...).toArray(): Cuando se usa Arrays.asList(...).toArray(), el tipo de componente del array resultante ahora es Object, no el tipo de los elementos del array subyacente. Por lo tanto, el siguiente código arroja un ClassCastException:

    String[] elements = (String[]) Arrays.asList("one", "two").toArray();
    

    En este caso, para conservar String como el tipo de componente en el array resultante, puedes usar Collection.toArray(Object[]) en su lugar:

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • Cambios en el manejo de códigos de idioma: Cuando se usa la API de Locale, los códigos de idioma para hebreo, yiddish e indonesio ya no se convierten a sus formas obsoletas (hebreo: iw, yiddish: ji e indonesio: in). Cuando especifiques el código de idioma de una de estas configuraciones regionales, usa los códigos de ISO 639-1 (hebreo: he, yiddish: yi e indonesio: id).

  • Cambios en las secuencias de int aleatorias: Después de los cambios realizados en https://bugs.openjdk.org/browse/JDK-8301574, los siguientes métodos Random.ints() ahora muestran una secuencia de números diferente a la de los métodos Random.nextInt():

    Por lo general, este cambio no debería provocar un comportamiento que interrumpa la app, pero tu código no debería esperar que la secuencia generada a partir de los métodos Random.ints() coincida con Random.nextInt().

La nueva API de SequencedCollection puede afectar la compatibilidad de tu app después de que actualices compileSdk en la configuración de compilación de tu app para usar Android 15 (nivel de API 35):

  • Colisión con las funciones de extensión MutableList.removeFirst() y MutableList.removeLast() en kotlin-stdlib

    El tipo List en Java se asigna al tipo MutableList en Kotlin. Debido a que las APIs de List.removeFirst() y List.removeLast() se introdujeron en Android 15 (nivel de API 35), el compilador de Kotlin resuelve las llamadas a función, por ejemplo, list.removeFirst(), de forma estática a las nuevas APIs de List en lugar de a las funciones de extensión en kotlin-stdlib.

    Si una app se vuelve a compilar con compileSdk establecido en 35 y minSdk establecido en 34 o una versión anterior, y luego se ejecuta en Android 14 y versiones anteriores, se genera un error de tiempo de ejecución:

    java.lang.NoSuchMethodError: No virtual method
    removeFirst()Ljava/lang/Object; in class Ljava/util/ArrayList;
    

    La opción de lint NewApi existente en el complemento de Android para Gradle puede detectar estos nuevos usos de la API.

    ./gradlew lint
    
    MainActivity.kt:41: Error: Call requires API level 35 (current min is 34): java.util.List#removeFirst [NewApi]
          list.removeFirst()
    

    Para corregir la excepción de tiempo de ejecución y los errores de lint, las llamadas a las funciones removeFirst() y removeLast() se pueden reemplazar por removeAt(0) y removeAt(list.lastIndex), respectivamente, en Kotlin. Si usas Android Studio Ladybug | 2024.1.3 o versiones posteriores, también proporciona una opción de solución rápida para estos errores.

    Considera quitar @SuppressLint("NewApi") y lintOptions { disable 'NewApi' } si se inhabilitó la opción lint.

  • Colisión con otros métodos en Java

    Se agregaron métodos nuevos a los tipos existentes, por ejemplo, List y Deque. Es posible que estos métodos nuevos no sean compatibles con los métodos que tienen el mismo nombre y los mismos tipos de argumentos en otras interfaces y clases. En el caso de una colisión de firma de método con incompatibilidad, el compilador javac muestra un error de tiempo de compilación. Por ejemplo:

    Ejemplo de error 1:

    javac MyList.java
    
    MyList.java:135: error: removeLast() in MyList cannot implement removeLast() in List
      public void removeLast() {
                  ^
      return type void is not compatible with Object
      where E is a type-variable:
        E extends Object declared in interface List
    

    Ejemplo de error 2:

    javac MyList.java
    
    MyList.java:7: error: types Deque<Object> and List<Object> are incompatible;
    public class MyList implements  List<Object>, Deque<Object> {
      both define reversed(), but with unrelated return types
    1 error
    

    Ejemplo de error 3:

    javac MyList.java
    
    MyList.java:43: error: types List<E#1> and MyInterface<E#2> are incompatible;
    public static class MyList implements List<Object>, MyInterface<Object> {
      class MyList inherits unrelated defaults for getFirst() from types List and MyInterface
      where E#1,E#2 are type-variables:
        E#1 extends Object declared in interface List
        E#2 extends Object declared in interface MyInterface
    1 error
    

    Para corregir estos errores de compilación, la clase que implementa estas interfaces debe reemplazar el método con un tipo de devolución compatible. Por ejemplo:

    @Override
    public Object getFirst() {
        return List.super.getFirst();
    }
    

Seguridad

Android 15 incluye cambios que promueven la seguridad del sistema para ayudar a proteger a las apps y a los usuarios de las apps maliciosas.

Inicios de actividades en segundo plano seguros

Android 15 protects users from malicious apps and gives them more control over their devices by adding changes that prevent malicious background apps from bringing other apps to the foreground, elevating their privileges, and abusing user interaction. Background activity launches have been restricted since Android 10 (API level 29).

Other changes

In addition to the restriction for UID matching, these other changes are also included:

  • Change PendingIntent creators to block background activity launches by default. This helps prevent apps from accidentally creating a PendingIntent that could be abused by malicious actors.
  • Don't bring an app to the foreground unless the PendingIntent sender allows it. This change aims to prevent malicious apps from abusing the ability to start activities in the background. By default, apps are not allowed to bring the task stack to the foreground unless the creator allows background activity launch privileges or the sender has background activity launch privileges.
  • Control how the top activity of a task stack can finish its task. If the top activity finishes a task, Android will go back to whichever task was last active. Moreover, if a non-top activity finishes its task, Android will go back to the home screen; it won't block the finish of this non-top activity.
  • Prevent launching arbitrary activities from other apps into your own task. This change prevents malicious apps from phishing users by creating activities that appear to be from other apps.
  • Block non-visible windows from being considered for background activity launches. This helps prevent malicious apps from abusing background activity launches to display unwanted or malicious content to users.

Intents más seguros

Android 15 presenta nuevas medidas de seguridad opcionales para que los intents sean más seguros y sólidos. El objetivo de estos cambios es evitar posibles vulnerabilidades y el uso inadecuado de intents que pueden aprovechar las apps maliciosas. Hay dos mejoras principales en la seguridad de los intents en Android 15:

  • Coincidir con filtros de intents de destino: Los intents que se orientan a componentes específicos deben coincidir de forma precisa con las especificaciones de filtros de intents del destino. Si envías un intent para iniciar la actividad de otra app, el componente de intent de destino debe alinearse con los filtros de intents declarados de la actividad receptora.
  • Los intents deben tener acciones: Los intents sin una acción ya no coincidirán con ningún filtro de intents. Esto significa que los intents que se usan para iniciar actividades o servicios deben tener una acción claramente definida.

Para verificar cómo responde tu app a estos cambios, usa StrictMode en tu app. Para ver registros detallados sobre los incumplimientos de uso de Intent, agrega el siguiente método:

Kotlin


fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        .detectUnsafeIntentLaunch()
        .build()
    )
}

Java


public void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
            .detectUnsafeIntentLaunch()
            .build());
}

Experiencia del usuario y IU del sistema

Android 15 incluye algunos cambios que tienen como objetivo crear una experiencia del usuario más intuitiva y coerente.

Cambios en la inserción de ventana

Hay dos cambios relacionados con las inserciones de ventana en Android 15: de borde a borde se aplica de forma predeterminada, y también hay cambios de configuración, como la configuración predeterminada de las barras del sistema.

Aplicación de borde a borde

Las apps son de borde a borde de forma predeterminada en dispositivos que ejecutan Android 15 si se orientan a Android 15 (nivel de API 35).

Una app orientada a Android 14 y no de borde a borde en un dispositivo con Android 15


Una app que se orienta a Android 15 (nivel de API 35) y es de borde a borde en un dispositivo con Android 15. Esta app usa principalmente componentes de Compose de Material 3 que aplican automáticamente inserciones. Esta pantalla no se ve afectada negativamente por la aplicación forzosa de borde a borde de Android 15.

Este es un cambio rotundo que podría afectar de forma negativa la IU de tu app. Los cambios afectan las siguientes áreas de la IU:

  • Barra de navegación con control de gestos
    • Es transparente de forma predeterminada.
    • El desplazamiento inferior está inhabilitado, por lo que el contenido se dibuja detrás de la barra de navegación del sistema, a menos que se apliquen las inserciones.
    • setNavigationBarColor y R.attr#navigationBarColor dejaron de estar disponibles y no afectan la navegación por gestos.
    • setNavigationBarContrastEnforced y R.attr#navigationBarContrastEnforced siguen sin tener efecto en la navegación por gestos.
  • Navegación con 3 botones
    • La opacidad está configurada en un 80% de forma predeterminada, y el color puede coincidir con el fondo de la ventana.
    • Se inhabilitó el desplazamiento inferior para que el contenido se dibuje detrás de la barra de navegación del sistema, a menos que se apliquen inserciones.
    • setNavigationBarColor y R.attr#navigationBarColor se configuran para que coincidan con el fondo de la ventana de forma predeterminada. El fondo de la ventana debe ser un elemento de diseño de color para que se aplique este valor predeterminado. Esta API dejó de estar disponible, pero sigue afectando la navegación de 3 botones.
    • setNavigationBarContrastEnforced y R.attr#navigationBarContrastEnforced son verdaderos de forma predeterminada, lo que agrega un fondo opaco del 80% en la navegación con 3 botones.
  • Barra de estado
    • Es transparente de forma predeterminada.
    • El desplazamiento superior está inhabilitado para que el contenido se dibuje detrás de la barra de estado, a menos que se apliquen inserciones.
    • setStatusBarColor y R.attr#statusBarColor dejaron de estar disponibles y no tienen ningún efecto en Android 15.
    • setStatusBarContrastEnforced y R.attr#statusBarContrastEnforced dejaron de estar disponibles, pero aún tienen un efecto en Android 15.
  • Recorte de la pantalla
    • layoutInDisplayCutoutMode de las ventanas no flotantes debe ser LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. SHORT_EDGES, NEVER y DEFAULT se interpretan como ALWAYS para que los usuarios no vean una barra negra causada por el corte de la pantalla y se muestren de borde a borde.

En el siguiente ejemplo, se muestra una app antes y después de orientarla a Android 15 (nivel de API 35), así como antes y después de aplicar las inserciones.

Una app que se orienta a Android 14 y no es de borde a borde en un dispositivo Android 15.
Una app que se orienta a Android 15 (nivel de API 35) y es de borde a borde en un dispositivo con Android 15. Sin embargo, muchos elementos ahora están ocultos por la barra de estado, la barra de navegación de 3 botones o el corte de pantalla debido a las aplicaciones forzosas de borde a borde de Android 15. La IU oculta incluye la barra superior de la app de Material 2, los botones de acción flotantes y los elementos de lista.
Una app orientada a Android 15 (nivel de API 35) está de borde a borde en un dispositivo con Android 15 y aplica inserciones para que la IU no quede oculta.
Qué verificar si tu app ya es de borde a borde

Si tu app ya es de borde a borde y aplica inserciones, tu mayor parte no será afectada, excepto en las siguientes situaciones. Sin embargo, incluso si crees que no te afectará, te recomendamos que pruebes tu app.

  • Tienes una ventana no flotante, como una Activity que usa SHORT_EDGES, NEVER o DEFAULT en lugar de LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. Si tu app falla durante el inicio, es posible que se deba a la pantalla de presentación. Puedes actualizar la dependencia de splashscreen principal a 1.2.0-alpha01 o una versión posterior, o bien establecer window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutInDisplayCutoutMode.always.
  • Puede haber pantallas con menos tráfico y una IU ocluida. Verifica que estas pantallas menos visitadas no tengan una IU ocluida. Entre las pantallas con menos tráfico, se incluyen las siguientes:
    • Pantallas de integración o acceso
    • Páginas de configuración
Qué verificar si tu app aún no es de borde a borde

Si tu app aún no es de borde a borde, es probable que se vea afectada. Además de las situaciones de las apps que ya son de borde a borde, debes tener en cuenta lo siguiente:

  • Si tu app usa componentes de Material 3 (androidx.compose.material3) en Compose, como TopAppBar, BottomAppBar y NavigationBar, es probable que estos componentes no se vean afectados porque controlan automáticamente las inserciones.
  • Si tu app usa componentes de Material 2 (androidx.compose.material) en Compose, estos no controlan las inserciones automáticamente. Sin embargo, puedes obtener acceso a las inserciones y aplicarlas manualmente. En androidx.compose.material 1.6.0 y versiones posteriores, usa el parámetro windowInsets para aplicar las inserciones de forma manual para BottomAppBar, TopAppBar, BottomNavigation y NavigationRail. De la misma manera, usa el parámetro contentWindowInsets para Scaffold.
  • Si tu app usa vistas y componentes de Material (com.google.android.material), la mayoría de los componentes de Material basados en vistas, como BottomNavigationView, BottomAppBar, NavigationRailView o NavigationView, controlan las inserciones y no requieren trabajo adicional. Sin embargo, debes agregar android:fitsSystemWindows="true" si usas AppBarLayout.
  • Para los elementos componibles personalizados, aplica las inserciones de forma manual como relleno. Si tu contenido está dentro de un Scaffold, puedes consumir inserciones con los valores de padding Scaffold. De lo contrario, aplica el padding con uno de los WindowInsets.
  • Si tu app usa vistas y BottomSheet, SideSheet o contenedores personalizados, aplica relleno con ViewCompat.setOnApplyWindowInsetsListener. Para RecyclerView, aplica padding con este objeto de escucha y, además, agrega clipToPadding="false".
Qué verifica si tu app debe ofrecer protección personalizada de fondo

Si tu app debe ofrecer una protección personalizada en segundo plano para la navegación con 3 botones o la barra de estado, debería colocar un elemento componible o una vista detrás de la barra del sistema con WindowInsets.Type#tappableElement() para obtener la altura de la barra de navegación de 3 botones o WindowInsets.Type#statusBars.

Recursos adicionales de borde a borde

Consulta las guías Views de borde a borde y Compose de borde a borde para obtener más información sobre la aplicación de inserciones.

APIs obsoletas

Las siguientes APIs dejaron de estar disponibles, pero no están inhabilitadas:

Las siguientes APIs dejaron de estar disponibles y están inhabilitadas:

Configuración estable

Si tu app está orientada a Android 15 (nivel de API 35) o versiones posteriores, Configuration ya no excluye las barras del sistema. Si usas el tamaño de pantalla en la clase Configuration para el cálculo del diseño, debes reemplazarlo por mejores alternativas, como un ViewGroup, WindowInsets o WindowMetricsCalculator apropiados según tus necesidades.

Configuration está disponible desde la API 1. Por lo general, se obtiene de Activity.onConfigurationChanged. Proporciona información como la densidad, orientación y tamaño de la ventana. Una característica importante de los tamaños de ventana que se muestran desde Configuration es que anteriormente excluía las barras del sistema.

El tamaño de configuración suele usarse para la selección de recursos, como /res/layout-h500dp, y este sigue siendo un caso de uso válido. Sin embargo, siempre se desaconsejó su uso para el cálculo del diseño. Si es así, debes alejarte ahora. Debes reemplazar el uso de Configuration por algo más adecuado según tu caso de uso.

Si lo usas para calcular el diseño, usa un ViewGroup adecuado, como CoordinatorLayout o ConstraintLayout. Si lo usas para determinar la altura de la barra de navegación del sistema, usa WindowInsets. Si quieres conocer el tamaño actual de la ventana de tu app, usa computeCurrentWindowMetrics.

En la siguiente lista, se describen los campos que se verán afectados por este cambio:

El atributo eleganteTextHeight se establece en verdadero de forma predeterminada

En el caso de las apps orientadas a Android 15, el atributo TextView elegantTextHeight se convierte en true de forma predeterminada, lo que reemplaza la fuente compacta de forma predeterminada con algunas secuencias de comandos que tienen métricas verticales grandes por una mucho más legible. La fuente compacta se introdujo para evitar diseños rotos. Android 13 (nivel de API 33) evita muchas de estas fallas permitiendo que el diseño de texto estire la altura vertical con el atributo fallbackLineSpacing.

En Android 15, la fuente compacta aún permanece en el sistema, por lo que tu app puede establecer elegantTextHeight en false para obtener el mismo comportamiento que antes, pero es poco probable que se admita en próximas versiones. Por lo tanto, si tu app admite las siguientes secuencias de comandos: árabe, laosiano, Birmania, tamil, guyaratí, canarés, malabar, oriya, telugu o tailandés, configura elegantTextHeight en true para probar la app.

El comportamiento de elegantTextHeight para las apps que se orientan a Android 14 (nivel de API 34) y versiones anteriores.
Comportamiento de elegantTextHeight para las apps orientadas a Android 15.

Cambios en el ancho de TextView para formas de letras complejas

In previous versions of Android, some cursive fonts or languages that have complex shaping might draw the letters in the previous or next character's area. In some cases, such letters were clipped at the beginning or ending position. Starting in Android 15, a TextView allocates width for drawing enough space for such letters and allows apps to request extra paddings to the left to prevent clipping.

Because this change affects how a TextView decides the width, TextView allocates more width by default if the app targets Android 15 (API level 35) or higher. You can enable or disable this behavior by calling the setUseBoundsForWidth API on TextView.

Because adding left padding might cause a misalignment for existing layouts, the padding is not added by default even for apps that target Android 15 or higher. However, you can add extra padding to preventing clipping by calling setShiftDrawingOffsetForStartOverhang.

The following examples show how these changes can improve text layout for some fonts and languages.

Standard layout for English text in a cursive font. Some of the letters are clipped. Here is the corresponding XML:

<TextView
    android:fontFamily="cursive"
    android:text="java" />
Layout for the same English text with additional width and padding. Here is the corresponding XML:

<TextView
    android:fontFamily="cursive"
    android:text="java"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />
Standard layout for Thai text. Some of the letters are clipped. Here is the corresponding XML:

<TextView
    android:text="คอมพิวเตอร์" />
Layout for the same Thai text with additional width and padding. Here is the corresponding XML:

<TextView
    android:text="คอมพิวเตอร์"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />

Altura de línea predeterminada de la configuración regional para EditText

En las versiones anteriores de Android, el diseño de texto estiraba la altura del texto para cumplir con la altura de la línea de la fuente que coincidía con la configuración regional actual. Por ejemplo, si el contenido estaba en japonés, debido a que la altura de la línea de la fuente japonesa es ligeramente mayor que la de una fuente latina, la altura del texto se agrandó un poco más grande. Sin embargo, a pesar de estas diferencias en las alturas de las líneas, el tamaño del elemento EditText se dimensionó de manera uniforme, independientemente de la configuración regional que se usara, como se ilustra en la siguiente imagen:

Tres cuadros que representan elementos EditText que pueden contener texto de inglés (en), japonés (ja) y birmano (my). La altura de EditText es la misma, aunque estos idiomas tienen alturas de línea diferentes.

En el caso de las apps orientadas a Android 15, ahora se reserva una altura de línea mínima para que EditText coincida con la fuente de referencia de la configuración regional especificada, como se muestra en la siguiente imagen:

Tres cuadros que representan elementos EditText que pueden contener texto de inglés (en), japonés (ja) y birmano (my). La altura de EditText ahora incluye espacio para adaptarse a la altura de línea predeterminada de las fuentes de estos idiomas.

Si es necesario, tu app puede restablecer el comportamiento anterior especificando el atributo useLocalePreferredLineHeightForMinimum en false, y tu app puede establecer métricas verticales mínimas personalizadas con la API de setMinimumFontMetrics en Kotlin y Java.

Cámara y contenido multimedia

Android 15 realiza los siguientes cambios en el comportamiento de la cámara y el contenido multimedia para las apps orientadas a Android 15 o versiones posteriores.

Restricciones para solicitar el foco de audio

Las apps orientadas a Android 15 deben ser la app principal o que ejecuten un servicio en primer plano para solicitar foco de audio. Si una app intenta solicitar el foco cuando no cumple con uno de estos requisitos, la llamada mostrará AUDIOFOCUS_REQUEST_FAILED.

Puedes obtener más información sobre el foco de audio en Cómo administrar el foco de audio.

Actualización de restricciones que no pertenecen al SDK

Android 15 incluye listas actualizadas de este tipo de interfaces que están basadas en la colaboración con desarrolladores de Android y las pruebas internas más recientes. Siempre que sea posible, nos aseguramos de que las alternativas públicas estén disponibles antes de restringir las interfaces que no pertenecen al SDK.

Si tu app no está orientada a Android 15, es posible que algunos de estos cambios no te afecten de inmediato. Sin embargo, si bien es posible que tu app acceda a algunas interfaces que no pertenecen al SDK según el nivel de API objetivo, usar cualquier método o campo que no pertenezca al SDK siempre implica un gran riesgo de error para tu app.

Si no sabes con seguridad si tu app usa este tipo de interfaces, puedes probarla para verificarlo. Si tu app depende de interfaces que no pertenecen al SDK, deberías planificar una migración hacia otras alternativas SDK, Sin embargo, sabemos que algunas apps tienen casos de uso válidos para usarlas. Si no encuentras una alternativa al uso de una interfaz que no pertenece al SDK para una función de tu app, deberías solicitar una nueva API pública.

Para obtener más información sobre los cambios implementados en esta versión de Android, consulta Actualizaciones a las restricciones de interfaces que no pertenecen al SDK en Android 15. Para obtener más información sobre interfaces que no pertenecen al SDK en general, consulta Restricciones en interfaces que no pertenecen al SDK.