Supporta il windowing del desktop

Le finestre delle app consentono agli utenti di eseguire più app contemporaneamente in finestre ridimensionabili per un'esperienza versatile, proprio come su un desktop.

Nella figura 1 puoi vedere l'organizzazione dello schermo con le app in finestra abilitate. Aspetti da considerare:

  • Gli utenti possono eseguire più app una accanto all'altra per utilizzarle contemporaneamente.
  • La barra delle app è in una posizione fissa nella parte inferiore del display e mostra le app in esecuzione. Gli utenti possono bloccare le app per accedervi rapidamente.
  • La nuova barra dell'intestazione personalizzabile decora la parte superiore di ogni finestra con controlli come Riduci a icona e Ingrandisci.
Il display di un tablet che mostra più app in esecuzione in finestre ridimensionabili con una barra delle app in basso.
Figura 1. Finestre delle app su un tablet.

Per impostazione predefinita, le app si aprono a schermo intero sui tablet Android. Per lanciare un'app in finestra, tieni premuto il punto di trascinamento della finestra nella parte superiore dello schermo e trascinalo all'interno dell'UI, come mostrato nella figura 2.

Quando un'app è aperta nelle finestre, anche le altre app si aprono in finestra.

Figura 2. Tieni premuto e trascina il punto di trascinamento dell'app per accedere alle finestre delle app.

Gli utenti possono anche richiamare le app in finestra dal menu visualizzato sotto il punto di trascinamento della finestra toccando o facendo clic sul punto di trascinamento, o utilizzando la scorciatoia da tastiera Tasto Meta (Windows, Comando o Ricerca) + Ctrl + Freccia giù.

Gli utenti possono uscire dalle finestre delle app chiudendo tutte le finestre attive o selezionando il punto di trascinamento nella parte superiore di un'app in finestra e trascinando l'app nella parte superiore dello schermo. Anche la scorciatoia da tastiera Meta + H consente di uscire dalle finestre delle app e di eseguire nuovamente le app in modalità a schermo intero.

Per tornare alle finestre delle app, tocca o seleziona il riquadro dello spazio del desktop nella schermata Recenti.

Ridimensionamento e modalità di compatibilità

Nelle finestre delle app, le app con orientamento bloccato possono essere ridimensionate liberamente. Ciò significa che, anche se un'attività è bloccata sull'orientamento verticale, gli utenti possono comunque ridimensionare l'app in una finestra con orientamento orizzontale.

Figura 3. Ridimensionamento della finestra di un'app con orientamento verticale in orizzontale.

L'UI delle app dichiarate come app non ridimensionabili (ovvero resizeableActivity = false) viene scalata mantenendo lo stesso formato.

Figura 4. L'UI di un'app non ridimensionabile viene scalata man mano che la finestra viene ridimensionata.

Le app della fotocamera che bloccano l'orientamento o sono dichiarate come non ridimensionabili hanno un trattamento speciale per i mirini della fotocamera: la finestra è completamente ridimensionabile, ma il mirino mantiene le stesse proporzioni. Se le app vengono sempre eseguite in verticale o orizzontale, le app vengono impostate come hardcoded o fanno altrimenti ipotesi che portano a errori di calcolo dell'orientamento o delle proporzioni dell'anteprima o dell'immagine acquisita, con conseguenti immagini allungate, laterali o capovolte.

Finché le app non saranno pronte a implementare mirini completamente reattivi, il trattamento speciale offre un'esperienza utente più semplice che mitiga gli effetti causati da ipotesi errate.

Per scoprire di più sulla modalità di compatibilità per le app fotocamera, consulta Modalità di compatibilità del dispositivo.

Figura 5. Il mirino della fotocamera mantiene le proporzioni durante il ridimensionamento della finestra.

Inset dell'intestazione personalizzabili

Tutte le app in esecuzione nelle finestre hanno una barra dell'intestazione, anche in modalità immersiva. Verifica che i contenuti dell'app non siano oscurati dalla barra dell'intestazione. La barra dell'intestazione è un tipo di inset della barra del titolo: WindowInsets.Companion.captionBar(); nelle visualizzazioni, WindowInsets.Type.captionBar(), che fa parte delle barre di sistema.

Puoi scoprire di più sulla gestione degli inset in Visualizza i contenuti edge-to-edge nell'app e gestisci gli inset della finestra in Compose.

Anche la barra dell'intestazione è personalizzabile. Android 15 ha introdotto il tipo di aspetto APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND per rendere la barra dell'intestazione trasparente e consentire alle app di disegnare contenuti personalizzati al suo interno.

Le app diventano quindi responsabili dello stile della parte superiore dei contenuti in modo che assomigli alla barra del titolo (sfondo, contenuti personalizzati e così via), ad eccezione degli elementi di sistema del titolo (pulsanti Chiudi e Ingrandisci), che vengono disegnati dal sistema sulla barra trasparente del titolo nella parte superiore dell'app.

Le app possono attivare/disattivare l'aspetto degli elementi di sistema all'interno del titolo per i temi chiaro e scuro utilizzando APPEARANCE_LIGHT_CAPTION_BARS, in modo simile a come vengono attivate/disattivate la barra di stato e la barra di navigazione.

Android 15 ha introdotto anche il metodo WindowInsets#getBoundingRects(), che consente alle app di esaminare in modo più dettagliato gli inset della barra del titolo. Le app possono distinguere tra le aree in cui il sistema disegna gli elementi di sistema e le aree inutilizzate in cui le app possono inserire contenuti personalizzati senza sovrapporsi agli elementi di sistema.

L'elenco degli oggetti Rect restituiti dall'API indica le regioni di sistema da evitare. L'app può disegnare senza sovrapporsi agli elementi di sistema e con la possibilità di ricevere input nello spazio rimanente (calcolato sottraendo i rettangoli degli inset della barra del titolo).

Chrome prima e dopo l'implementazione delle intestazioni personalizzate.
Figura 6. Chrome prima e dopo l'implementazione delle intestazioni personalizzate.

Per impostare i rettangoli di esclusione dei gesti di sistema per un'intestazione personalizzata, implementa quanto segue nella visualizzazione o nel componibile:

// In a custom View's onLayout or a similar lifecycle method
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    super.onLayout(changed, left, top, right, bottom)
    if (changed) {
        // Calculate the height of your custom header
        val customHeaderHeight = 100 // Replace with your actual header height in pixels

        // Create a Rect covering your custom header area
        val exclusionRect = Rect(0, 0, width, customHeaderHeight)

        // Set the exclusion rects for the system
        systemGestureExclusionRects = listOf(exclusionRect)
    }
}

Supporto per multitasking e multi-istanza

Il multitasking è lo scopo principale delle finestre delle app; consentire più istanze della tua app può aumentare notevolmente la produttività degli utenti.

Android 15 introduce PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, che le app possono impostare per specificare che la UI di sistema deve essere mostrata per consentire l'avvio dell'app come più istanze.

Puoi dichiarare PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI nel AndroidManifest.xml della tua app all'interno del tag <activity>:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:resizeableActivity="true">
    <meta-data
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</activity>

Gestisci le istanze delle app con i gesti di trascinamento

In modalità multi-finestra, gli utenti possono avviare una nuova istanza dell'app trascinando un elemento di visualizzazione fuori dalla finestra dell'app. Gli utenti possono anche spostare gli elementi tra le istanze della stessa app.

Figura 7. Avvia una nuova istanza di Chrome trascinando una scheda fuori dalle finestre delle app.

Android 15 introduce due flag per personalizzare il comportamento di trascinamento:

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: indica che un trascinamento non gestito deve essere delegato al sistema per essere avviato se nessuna finestra visibile gestisce il rilascio. Quando utilizzi questo flag, il chiamante deve fornire ClipData con un ClipData.Item che contiene un IntentSender immutabile a un'attività da avviare (vedi ClipData.Item.Builder#setIntentSender()). Il sistema può avviare l'intent o meno in base a fattori come le dimensioni dello schermo o la modalità finestra correnti. Se il sistema non avvia l'intent, l'intent viene annullato tramite il normale flusso di trascinamento.

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: indica che un'operazione di trascinamento può superare i limiti della finestra (per più istanze della stessa applicazione).

    Quando [startDragAndDrop()][20] viene chiamato con questo flag impostato, solo le finestre visibili appartenenti alla stessa applicazione possono partecipare all'operazione di trascinamento e ricevere i contenuti trascinati.

L'esempio seguente mostra come utilizzare questi flag con startDragAndDrop():

// Assuming 'view' is the View that initiates the drag
view.setOnLongClickListener {
    // Create an IntentSender for the activity you want to launch
    val launchIntent = Intent(view.context, NewInstanceActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(
        view.context,
        0,
        launchIntent,
        PendingIntent.FLAG_IMMUTABLE // Ensure the PendingIntent is immutable
    )

    // Build the ClipData.Item with the IntentSender
    val item = ClipData.Item.Builder()
        .setIntentSender(pendingIntent.intentSender)
        .build()

    // Create ClipData with a simple description and the item
    val dragData = ClipData(
        ClipDescription("New Instance Drag", arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)),
        item
    )

    // Combine the drag flags
    val dragFlags = View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG or
                    View.DRAG_FLAG_GLOBAL_SAME_APPLICATION

    // Start the drag operation
    view.startDragAndDrop(
        dragData,                     // The ClipData to drag
        View.DragShadowBuilder(view), // A visual representation of the dragged item
        null,                         // Local state object (not used here)
        dragFlags                     // The drag flags
    )
    true // Indicate that the long click was consumed
}
Figura 8. Spostamento di una scheda tra due istanze dell'app Chrome.

Altre ottimizzazioni

Personalizza l'avvio delle app e la transizione dalle app dalla visualizzazione a finestre alla modalità a schermo intero.

Specifica dimensioni e posizione predefinite

Non tutte le app hanno bisogno di una finestra grande per offrire valore all'utente, anche se sono ridimensionabili. Puoi utilizzare il metodo ActivityOptions#setLaunchBounds() per specificare una dimensione e una posizione predefinite all'avvio di un'attività.

Ecco un esempio di come impostare i limiti di avvio per un'attività:

val options = ActivityOptions.makeBasic()

// Define the desired launch bounds (left, top, right, bottom in pixels)
val launchBounds = Rect(100, 100, 700, 600) // Example: 600x500 window at (100,100)

// Apply the launch bounds to the ActivityOptions
options.setLaunchBounds(launchBounds)

// Start the activity with the specified options
val intent = Intent(this, MyActivity::class.java)
startActivity(intent, options.toBundle())

Attiva la modalità a schermo intero dallo spazio del desktop

Le app possono passare alla modalità a schermo intero chiamando Activity#requestFullScreenMode(). Il metodo mostra l'app a schermo intero direttamente dalle app in finestra.

Per richiedere la modalità a schermo intero da un'attività, utilizza il seguente codice:

// In an Activity
fun enterFullScreen() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // Android 15 (U)
        requestFullScreenMode()
    }
}