Trascina in modalità multi-finestra

I dispositivi con Android 7.0 (livello API 24) o versioni successive supportano la modalità multi-finestra personalizzata, che consente agli utenti Spostare dati da un'app all'altra trascinandoli.

L'app di origine, in cui viene avviata l'operazione, fornisce i dati. L'app di destinazione, in cui termina l'operazione, riceve i dati.

Quando l'utente inizia a trascinare i contenuti, l'app di origine deve impostare la DRAG_FLAG_GLOBAL per indicare che l'utente può trascinare i dati in un'altra app.

Poiché i dati si spostano oltre i confini delle app, queste condividono l'accesso ai dati utilizzando un URI contenuti. Ciò richiede quanto segue:

  • L'app di origine deve impostare una o entrambe le opzioni DRAG_FLAG_GLOBAL_URI_READ e DRAG_FLAG_GLOBAL_URI_WRITE , a seconda dell'accesso in lettura o scrittura ai dati che l'app di origine vuole concedere l'autorizzazione all'app di destinazione.
  • L'app di destinazione deve chiamare requestDragAndDropPermissions() immediatamente prima di gestire i dati che l'utente trascina nell'app. Se l'app di destinazione non ha più bisogno di accedere ai dati trascinati, può quindi chiama release() attivo l'oggetto restituito da requestDragAndDropPermissions(). Altrimenti, le autorizzazioni vengono rilasciate quando l'attività contenitore distrutte. Se la tua implementazione prevede l'avvio di una nuova attività per elaborare URL eliminati, dovrai concedere alla nuova attività le stesse autorizzazioni. Devi impostare i dati del clip e un flag:

    Kotlin

    intent.setClipData(clipData)
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    

    Java

    intent.setClipData(clipData);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    

I seguenti snippet di codice mostrano come concedere l'accesso in sola lettura a i dati trascinati subito dopo aver eseguito l'operazione di rilascio dell'utente. Consulta le Demo di trascinamento su GitHub per un esempio più completo.

Attività di origine

Kotlin

// Drag a file stored in an images/ directory in internal storage.
val internalImagesDir = File(context.filesDir, "images")
val imageFile = File(internalImagesDir, imageFilename)
val uri = FileProvider.getUriForFile(context, contentAuthority, imageFile)

val listener = OnDragStartListener@{ view: View, _: DragStartHelper ->
    val clipData = ClipData(ClipDescription("Image Description",
                                            arrayOf("image/*")),
                            ClipData.Item(uri))
    // Must include DRAG_FLAG_GLOBAL to permit dragging data between apps.
    // This example provides read-only access to the data.
    val flags = View.DRAG_FLAG_GLOBAL or View.DRAG_FLAG_GLOBAL_URI_READ
    return@OnDragStartListener view.startDragAndDrop(clipData,
                                                     View.DragShadowBuilder(view),
                                                     null,
                                                     flags)
}

// Container where the image originally appears in the source app.
val srcImageView = findViewById<ImageView>(R.id.imageView)

// Detect and start the drag event.
DragStartHelper(srcImageView, listener).apply {
    attach()
}

Java

// Drag a file stored in an images/ directory in internal storage.
File internalImagesDir = new File(context.getFilesDir(), "images");
File imageFile = new File(internalImagesDir, imageFilename);
final Uri uri = FileProvider.getUriForFile(context, contentAuthority, imageFile);

// Container where the image originally appears in the source app.
ImageView srcImageView = findViewById(R.id.imageView);

// Enable the view to detect and start the drag event.
new DragStartHelper(srcImageView, (view, helper) -> {
    ClipData clipData = new ClipData(new ClipDescription("Image Description",
                                                          new String[] {"image/*"}),
                                     new ClipData.Item(uri));
    // Must include DRAG_FLAG_GLOBAL to permit dragging data between apps.
    // This example provides read-only access to the data.
    int flags = View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ;
    return view.startDragAndDrop(clipData,
                                 new View.DragShadowBuilder(view),
                                 null,
                                 flags);
}).attach();

Attività target

Kotlin

// Container where the image is to be dropped in the target app.
val targetImageView = findViewById<ImageView>(R.id.imageView)

targetImageView.setOnDragListener { view, event ->

    when (event.action) {

        ACTION_DROP -> {
            val imageItem: ClipData.Item = event.clipData.getItemAt(0)
            val uri = imageItem.uri

            // Request permission to access the image data being dragged into
            // the target activity's ImageView element.
            val dropPermissions = requestDragAndDropPermissions(event)
            (view as ImageView).setImageURI(uri)

            // Release the permission immediately afterward because it's no
            // longer needed.
            dropPermissions.release()
            return@setOnDragListener true
        }

        // Implement logic for other DragEvent cases here.

        // An unknown action type is received.
        else -> {
            Log.e("DragDrop Example", "Unknown action type received by View.OnDragListener.")
            return@setOnDragListener false
        }

    }
}

Java

// Container where the image is to be dropped in the target app.
ImageView targetImageView = findViewById(R.id.imageView);

targetImageView.setOnDragListener( (view, event) -> {

    switch (event.getAction()) {

        case ACTION_DROP:
            ClipData.Item imageItem = event.getClipData().getItemAt(0);
            Uri uri = imageItem.getUri();

            // Request permission to access the image data being dragged into
            // the target activity's ImageView element.
            DragAndDropPermissions dropPermissions =
                requestDragAndDropPermissions(event);

            ((ImageView)view).setImageURI(uri);

            // Release the permission immediately afterward because it's no
            // longer needed.
            dropPermissions.release();

            return true;

        // Implement logic for other DragEvent cases here.

        // An unknown action type was received.
        default:
            Log.e("DragDrop Example","Unknown action type received by View.OnDragListener.");
            break;
    }

    return false;
});