Copier et coller

Android fournit un framework puissant basé sur un presse-papiers pour copier et coller des éléments. Il prend en charge des et types de données complexes, y compris les chaînes de texte, les structures de données complexes, le texte et les flux binaires des données et des éléments d'application. Les données textuelles simples sont stockées directement dans le presse-papiers, tandis que les données les données sont stockées en tant que référence que l'application de collage résout avec un fournisseur de contenu. Copie... et de coller fonctionne à la fois au sein d'une application et entre des applications qui mettent en œuvre d'infrastructure.

Étant donné qu'une partie du framework fait appel à des fournisseurs de contenu, ce document suppose une certaine connaissance de l'API du fournisseur de contenu Android, décrite dans Fournisseurs de contenu :

Les utilisateurs s'attendent à recevoir un retour lorsqu'ils copient du contenu dans le presse-papiers. Ainsi, en plus du framework, permet de copier-coller, Android affiche une interface utilisateur par défaut pour les utilisateurs lors de la copie dans Android 13 (niveau d'API 33) et supérieurs. En raison de cette fonctionnalité, vous risquez de recevoir des notifications en double. Pour en savoir plus sur ce cas limite, dans le Éviter les notifications en double .

Animation montrant une notification du presse-papiers Android 13
Figure 1 : UI affichée lorsque du contenu entre dans le presse-papiers dans Android 13 ans et plus.

Envoyez manuellement des commentaires aux utilisateurs lors de la copie dans Android 12L (niveau d'API 32) ou version antérieure. Voir recommandations dans ce document.

Structure du presse-papiers

Lorsque vous utilisez le framework du presse-papiers, placez les données dans un objet clip, puis placez cet objet clip. dans le presse-papiers du système. L'objet clip peut prendre l'une des trois formes suivantes:

Texte
Chaîne de texte. Placez la chaîne directement dans l'objet clip, que vous placez ensuite sur la presse-papiers. Pour coller la chaîne, récupérez l'objet clip dans le presse-papiers et copiez la dans l'espace de stockage de votre application.
URI
Un objet Uri représentant n'importe quelle sous forme d'URI. Cette opération sert principalement à copier des données complexes à partir d'un fournisseur de contenu. Copier des données, placez un objet Uri dans un objet Clip, puis placez cet objet sur la presse-papiers. Pour coller les données, récupérez l'objet clip et obtenez l'objet Uri, la résoudre vers une source de données, telle qu'un fournisseur de contenu, et copier les données dans l'espace de stockage de votre application.
Intent
Une Intent. Ce prend en charge la copie de raccourcis d'application. Pour copier des données, créez un Intent, placez dans un objet clip et placez cet objet clip dans le presse-papiers. Pour coller les données, l'objet extrait, puis copiez l'objet Intent dans le fichier dans la zone de mémoire dédiée.

Le presse-papiers ne contient qu'un élément clip à la fois. Lorsqu'une application place un objet clip sur la presse-papiers, l'objet clip précédent disparaît.

Si vous souhaitez autoriser les utilisateurs à coller des données dans votre application, vous n'avez pas à gérer tous les types données. Vous pouvez examiner les données du presse-papiers avant d'offrir aux utilisateurs la possibilité de les coller. En plus d'avoir une certaine forme de données, l'objet clip contient des métadonnées qui vous indiquent sont disponibles. Ces métadonnées vous aident à décider si votre application peut faire quelque chose d'utile avec les données du presse-papiers. Par exemple, si l'une de vos applications traite principalement du texte, peut vouloir ignorer les objets clip qui contiennent un URI ou un intent.

Vous pouvez également autoriser les utilisateurs à coller du texte, quelle que soit la forme des données contenues dans le presse-papiers. À ce faire, forcer les données du presse-papiers dans une représentation textuelle, puis coller ce texte. C'est décrit dans la section Coercer le presse-papiers en texte.

Classes dans le presse-papiers

Cette section décrit les classes utilisées par le framework du presse-papiers.

Gestionnaire du presse-papiers

Le presse-papiers du système Android est représenté par l'icône ClipboardManager. N'instanciez pas cette classe directement. Au lieu de cela, obtenez une référence à celui-ci en appelant getSystemService(CLIPBOARD_SERVICE)

ClipData, ClipData.Item et ClipDescription

Pour ajouter des données au presse-papiers, créez un Objet ClipData contenant une description des données et des données elles-mêmes. Le presse-papiers contient un ClipData à une en temps réel. Un ClipData contient un Objet ClipDescription et un ou plusieurs Objets ClipData.Item.

Un objet ClipDescription contient des métadonnées sur l'extrait. En particulier, il contient un tableau des types MIME disponibles pour les données de l'extrait. De plus, sur Android 12 (niveau d'API 31) ou version ultérieure, les métadonnées indiquent si l'objet contient texte stylisé et à propos des le type de texte dans l'objet. Lorsque vous placez un extrait dans le presse-papiers, ces informations peuvent être collées dans des applications, ce qui peuvent vérifier s'ils peuvent gérer les données des extraits.

Un objet ClipData.Item contient les données de texte, d'URI ou d'intent:

Texte
Un CharSequence.
URI
Un Uri. Il contient généralement un URI de fournisseur de contenu, bien que tout URI soit autorisé. L'application qui fournit les données place l'URI dans le presse-papiers. Applications qui souhaitent coller les données, récupèrent l'URI du presse-papiers et l'utilisent pour accéder au contenu ou une autre source de données, et vous récupérez les données.
Intent
Une Intent. Ce type de données vous permet de copier un raccourci d'application presse-papiers. Les utilisateurs peuvent ensuite coller le raccourci dans leurs applications pour une utilisation ultérieure.

Vous pouvez ajouter plusieurs objets ClipData.Item à un extrait. Cela permet aux utilisateurs de copier et coller plusieurs sélections dans un seul clip. Par exemple, si vous disposez d'un widget de liste l'utilisateur sélectionne plusieurs éléments à la fois, vous pouvez copier tous les éléments dans le presse-papiers en même temps. À faire créez un ClipData.Item distinct pour chaque élément de la liste, puis ajoutez ClipData.Item à l'objet ClipData.

Méthodes pratiques ClipData

La classe ClipData fournit des méthodes statiques pour créer un un objet ClipData avec un seul objet ClipData.Item et un objet Objet ClipDescription:

newPlainText(label, text)
Renvoie un objet ClipData dont l'objet ClipData.Item unique contient une chaîne de texte. Le libellé de l'objet ClipDescription est défini sur label Le type MIME unique dans ClipDescription est MIMETYPE_TEXT_PLAIN

Utilisez newPlainText() pour créer un extrait à partir d'une chaîne de texte.

newUri(resolver, label, URI)
Renvoie un objet ClipData dont l'objet ClipData.Item unique contient un URI. Le libellé de l'objet ClipDescription est défini sur label Si l'URI est un URI de contenu, c'est-à-dire que si Uri.getScheme() renvoie content: : la méthode utilise le ContentResolver fourni dans resolver pour récupérer les types MIME disponibles à partir du fournisseur de contenu. Il les stocke ensuite dans ClipDescription. Pour un URI qui n'est pas un URI content:, la méthode définit le type MIME sur MIMETYPE_TEXT_URILIST

Utilisez newUri() pour créer un extrait à partir d'un URI, en particulier URI content:.

newIntent(label, intent)
Renvoie un objet ClipData dont l'objet ClipData.Item unique contient un Intent. Le libellé de l'objet ClipDescription est défini sur label Le type MIME est défini sur MIMETYPE_TEXT_INTENT

Utilisez newIntent() pour créer un extrait à partir d'un objet Intent.

Convertir les données du presse-papiers en texte

Même si votre application ne gère que le texte, vous pouvez copier des données non textuelles du presse-papiers en en le convertissant ClipData.Item.coerceToText() .

Cette méthode convertit les données de ClipData.Item en texte et renvoie une CharSequence La valeur renvoyée par ClipData.Item.coerceToText() est basée sous la forme de données dans ClipData.Item:

Texte
Si ClipData.Item correspond à du texte, c'est-à-dire si getText() n'est pas nulle (coerceToText() renvoie le texte).
URI
Si ClipData.Item est un URI, c'est-à-dire, si getUri() n'est pas nulle : coerceToText() tente de l'utiliser comme URI de contenu.
  • Si l'URI est un URI de contenu et que le fournisseur peut renvoyer un flux de texte, coerceToText() renvoie un flux de texte.
  • Si l'URI est un URI de contenu, mais que le fournisseur ne propose pas de flux de texte, coerceToText() renvoie une représentation de l'URI. La représentation est identique à celle renvoyée par Uri.toString()
  • Si l'URI n'est pas un URI de contenu, coerceToText() renvoie une représentation de l'URI. La représentation est la même que celle renvoyée par Uri.toString()
Intent
Si ClipData.Item est de type Intent, c'est-à-dire si getIntent() n'est pas nulle : coerceToText() le convertit en URI d'intent et le renvoie. La représentation est la même que celle renvoyée par Intent.toUri(URI_INTENT_SCHEME)

Le framework du presse-papiers est résumé dans la figure 2. Pour copier des données, une application place ClipData dans le presse-papiers global ClipboardManager. La ClipData contient un ou plusieurs objets ClipData.Item et un ClipDescription. Pour coller des données, une application obtient ClipData, obtient son type MIME à partir de ClipDescription, et les données sont extraites de la ClipData.Item ou du fournisseur de contenu mentionné par ClipData.Item

<ph type="x-smartling-placeholder">
</ph> Image montrant un diagramme de blocs du framework de copier-coller <ph type="x-smartling-placeholder">
</ph> Figure 2 : Framework du presse-papiers Android.

Copier dans le presse-papiers

Pour copier des données dans le presse-papiers, obtenez un handle vers l'objet ClipboardManager global : créez un objet ClipData, puis ajoutez un élément ClipDescription et un ou plusieurs ClipData.Item. Ajoutez ensuite l'objet ClipData terminé à ClipboardManager. Ce processus est décrit plus en détail dans la procédure suivante:

  1. Si vous copiez des données à l'aide d'un URI de contenu, configurez un fournisseur de contenu.
  2. Récupérez le presse-papiers du système:

    Kotlin

    when(menuItem.itemId) {
        ...
        R.id.menu_copy -> { // if the user selects copy
            // Gets a handle to the clipboard service.
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        }
    }
    

    Java

    ...
    // If the user selects copy.
    case R.id.menu_copy:
    
    // Gets a handle to the clipboard service.
    ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
    
  3. Copiez les données dans un nouvel objet ClipData:

    • Texte

      Kotlin

      // Creates a new text clip to put on the clipboard.
      val clip: ClipData = ClipData.newPlainText("simple text", "Hello, World!")
      

      Java

      // Creates a new text clip to put on the clipboard.
      ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");
      
    • Pour un URI

      Cet extrait construit un URI en encodant un ID d'enregistrement dans l'URI de contenu de le fournisseur. Cette technique est abordée plus en détail dans le Encoder un identifiant dans la section URI

      Kotlin

      // Creates a Uri using a base Uri and a record ID based on the contact's last
      // name. Declares the base URI string.
      const val CONTACTS = "content://com.example.contacts"
      
      // Declares a path string for URIs, used to copy data.
      const val COPY_PATH = "/copy"
      
      // Declares the Uri to paste to the clipboard.
      val copyUri: Uri = Uri.parse("$CONTACTS$COPY_PATH/$lastName")
      ...
      // Creates a new URI clip object. The system uses the anonymous
      // getContentResolver() object to get MIME types from provider. The clip object's
      // label is "URI", and its data is the Uri previously created.
      val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
      

      Java

      // Creates a Uri using a base Uri and a record ID based on the contact's last
      // name. Declares the base URI string.
      private static final String CONTACTS = "content://com.example.contacts";
      
      // Declares a path string for URIs, used to copy data.
      private static final String COPY_PATH = "/copy";
      
      // Declares the Uri to paste to the clipboard.
      Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName);
      ...
      // Creates a new URI clip object. The system uses the anonymous
      // getContentResolver() object to get MIME types from provider. The clip object's
      // label is "URI", and its data is the Uri previously created.
      ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
      
    • Pour un intent

      Cet extrait construit un Intent pour une application, puis place dans l'objet clip:

      Kotlin

      // Creates the Intent.
      val appIntent = Intent(this, com.example.demo.myapplication::class.java)
      ...
      // Creates a clip object with the Intent in it. Its label is "Intent"
      // and its data is the Intent object created previously.
      val clip: ClipData = ClipData.newIntent("Intent", appIntent)
      

      Java

      // Creates the Intent.
      Intent appIntent = new Intent(this, com.example.demo.myapplication.class);
      ...
      // Creates a clip object with the Intent in it. Its label is "Intent"
      // and its data is the Intent object created previously.
      ClipData clip = ClipData.newIntent("Intent", appIntent);
      
  4. Placez le nouvel objet clip dans le presse-papiers:

    Kotlin

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip)
    

    Java

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip);
    

Envoyer des commentaires lors de la copie dans le presse-papiers

Les utilisateurs s'attendent à un retour visuel lorsqu'une application copie du contenu dans le presse-papiers. C'est fait automatiquement pour les utilisateurs d'Android 13 ou version ultérieure, mais elle doit être implémentée manuellement versions.

À partir d'Android 13, le système affiche une confirmation visuelle standard lorsque du contenu est ajouté. dans le presse-papiers. La nouvelle confirmation effectue les opérations suivantes:

  • confirme que le contenu a bien été copié.
  • Fournit un aperçu du contenu copié.

Animation montrant une notification du presse-papiers Android 13
Figure 3 : UI affichée lorsque du contenu entre dans le presse-papiers dans Android 13 ans et plus.

Sous Android 12L (niveau d'API 32) ou version antérieure, les utilisateurs peuvent ne pas être sûrs d'avoir copié correctement. contenus ou copiés. Cette fonctionnalité standardise les différentes notifications affichées par les applications après copier et offre aux utilisateurs plus de contrôle sur le presse-papiers.

Éviter les notifications en double

Sur Android 12L (niveau d'API 32) ou version antérieure, nous vous recommandons d'alerter les utilisateurs lorsqu'ils ont réussi à copier en envoyant des commentaires visuels dans l'application, à l'aide d'un widget comme un Toast ou un Snackbar, après la copie.

Pour éviter l'affichage en double des informations, nous vous recommandons vivement de supprimer les toasts ou des snackbars qui s'affichent après une copie intégrée à l'application pour Android 13 ou version ultérieure.

Post snackbar après une copie dans l&#39;application.
Figure 4 : Si vous affichez un snackbar de confirmation de la copie sous Android 13, l'utilisateur voit des messages en double.
<ph type="x-smartling-placeholder">
</ph> Publier un toast après une copie dans l&#39;application. <ph type="x-smartling-placeholder">
</ph> Figure 5 : Si vous affichez un toast de confirmation de copie sous Android 13, l'utilisateur voit des messages en double.

Voici un exemple de mise en œuvre:

fun textCopyThenPost(textCopied:String) {
    val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
    // When setting the clipboard text.
    clipboardManager.setPrimaryClip(ClipData.newPlainText   ("", textCopied))
    // Only show a toast for Android 12 and lower.
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2)
        Toast.makeText(context, “Copied”, Toast.LENGTH_SHORT).show()
}

Ajouter du contenu sensible dans le presse-papiers

Si votre application permet aux utilisateurs de copier du contenu sensible, comme des mots de passe ou des crédits, dans le presse-papiers informations de la carte de paiement, vous devez ajouter un indicateur à ClipDescription dans ClipData avant d'appeler ClipboardManager.setPrimaryClip(). L'ajout de cet indicateur empêche les données le contenu d'apparaître dans la confirmation visuelle de contenu copié sous Android 13 et versions ultérieures.

Aperçu du texte copié sans signaler le contenu sensible
Figure 6. Aperçu du texte copié sans indicateur de contenu sensible.
<ph type="x-smartling-placeholder">
</ph> Aperçu du texte copié signalant le contenu sensible. <ph type="x-smartling-placeholder">
</ph> Figure 7 : Aperçu du texte copié avec un indicateur de contenu sensible.

Pour signaler du contenu sensible, ajoutez un extra booléen à ClipDescription. Toutes les applis doivent le faire quel que soit le niveau d'API ciblé.

// If your app is compiled with the API level 33 SDK or higher.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
    }
}

// If your app is compiled with a lower SDK.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean("android.content.extra.IS_SENSITIVE", true)
    }
}

Coller depuis le presse-papiers

Comme décrit précédemment, collez les données du presse-papiers en obtenant l'objet de presse-papiers global, obtenir l'objet clip, examiner ses données et, si possible, les copier à partir de cet objet clip dans votre propre espace de stockage. Cette section explique en détail comment coller les trois formes de presse-papiers données.

<ph type="x-smartling-placeholder">

Coller le texte brut

Pour coller du texte brut, récupérez le presse-papiers global et vérifiez qu'il peut renvoyer du texte brut. Ensuite, l'objet extrait et copiez son texte dans votre espace de stockage à l'aide de getText(), comme décrit dans la procédure suivante:

  1. Récupérez l'objet ClipboardManager global en utilisant getSystemService(CLIPBOARD_SERVICE) Déclarez également une variable globale contenant le texte collé:

    Kotlin

    var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    var pasteData: String = ""
    

    Java

    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    String pasteData = "";
    
  2. Déterminez si vous devez activer ou désactiver le "coller" dans la configuration activité. Vérifiez que le presse-papiers contient un extrait et que vous pouvez gérer le type de données représentées par l'extrait:

    Kotlin

    // Gets the ID of the "paste" menu item.
    val pasteItem: MenuItem = menu.findItem(R.id.menu_paste)
    
    // If the clipboard doesn't contain data, disable the paste menu item.
    // If it does contain data, decide whether you can handle the data.
    pasteItem.isEnabled = when {
        !clipboard.hasPrimaryClip() -> {
            false
        }
        !(clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) -> {
            // Disables the paste menu item, since the clipboard has data but it
            // isn't plain text.
            false
        }
        else -> {
            // Enables the paste menu item, since the clipboard contains plain text.
            true
        }
    }
    

    Java

    // Gets the ID of the "paste" menu item.
    MenuItem pasteItem = menu.findItem(R.id.menu_paste);
    
    // If the clipboard doesn't contain data, disable the paste menu item.
    // If it does contain data, decide whether you can handle the data.
    if (!(clipboard.hasPrimaryClip())) {
    
        pasteItem.setEnabled(false);
    
    } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) {
    
        // Disables the paste menu item, since the clipboard has data but
        // it isn't plain text.
        pasteItem.setEnabled(false);
    } else {
    
        // Enables the paste menu item, since the clipboard contains plain text.
        pasteItem.setEnabled(true);
    }
    
  3. Copiez les données du presse-papiers. Ce point du code n'est accessible que si "coller" est activé. Vous pouvez donc supposer que le presse-papiers contient des fichiers texte. Vous ne savez pas encore s'il contient une chaîne de texte ou un URI qui pointe vers du texte brut. L'extrait de code suivant teste cela, mais n'affiche que le code pour le traitement du texte brut:

    Kotlin

    when (menuItem.itemId) {
        ...
        R.id.menu_paste -> {    // Responds to the user selecting "paste".
            // Examines the item on the clipboard. If getText() doesn't return null,
            // the clip item contains the text. Assumes that this application can only
            // handle one item at a time.
            val item = clipboard.primaryClip.getItemAt(0)
    
            // Gets the clipboard as text.
            pasteData = item.text
    
            return if (pasteData != null) {
                // If the string contains data, then the paste operation is done.
                true
            } else {
                // The clipboard doesn't contain text. If it contains a URI,
                // attempts to get data from it.
                val pasteUri: Uri? = item.uri
    
                if (pasteUri != null) {
                    // If the URI contains something, try to get text from it.
    
                    // Calls a routine to resolve the URI and get data from it.
                    // This routine isn't presented here.
                    pasteData = resolveUri(pasteUri)
                    true
                } else {
    
                    // Something is wrong. The MIME type was plain text, but the
                    // clipboard doesn't contain text or a Uri. Report an error.
                    Log.e(TAG,"Clipboard contains an invalid data type")
                    false
                }
            }
        }
    }
    

    Java

    // Responds to the user selecting "paste".
    case R.id.menu_paste:
    
    // Examines the item on the clipboard. If getText() does not return null,
    // the clip item contains the text. Assumes that this application can only
    // handle one item at a time.
     ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
    
    // Gets the clipboard as text.
    pasteData = item.getText();
    
    // If the string contains data, then the paste operation is done.
    if (pasteData != null) {
        return true;
    
    // The clipboard doesn't contain text. If it contains a URI, attempts to get
    // data from it.
    } else {
        Uri pasteUri = item.getUri();
    
        // If the URI contains something, try to get text from it.
        if (pasteUri != null) {
    
            // Calls a routine to resolve the URI and get data from it.
            // This routine isn't presented here.
            pasteData = resolveUri(Uri);
            return true;
        } else {
    
            // Something is wrong. The MIME type is plain text, but the
            // clipboard doesn't contain text or a Uri. Report an error.
            Log.e(TAG, "Clipboard contains an invalid data type");
            return false;
        }
    }
    

Coller les données à partir d'un URI de contenu

Si l'objet ClipData.Item contient un URI de contenu et que vous estimez que vous pouvez gérer l'un de ses types MIME, créer un ContentResolver et appeler le contenu approprié pour récupérer les données.

La procédure suivante explique comment récupérer des données auprès d'un fournisseur de contenu en fonction d'un URI de contenu dans le presse-papiers. Elle vérifie si un type MIME que l'application peut utiliser est disponible à partir du un fournisseur de services agréé.

  1. Déclarez une variable globale destinée à contenir le type MIME:

    Kotlin

    // Declares a MIME type constant to match against the MIME types offered
    // by the provider.
    const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
    

    Java

    // Declares a MIME type constant to match against the MIME types offered by
    // the provider.
    public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
    
  2. Récupérez le presse-papiers global. Vous pouvez également obtenir un résolveur de contenu pour accéder au contenu fournisseur:

    Kotlin

    // Gets a handle to the Clipboard Manager.
    val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
    // Gets a content resolver instance.
    val cr = contentResolver
    

    Java

    // Gets a handle to the Clipboard Manager.
    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    
    // Gets a content resolver instance.
    ContentResolver cr = getContentResolver();
    
  3. Récupérez l'extrait principal dans le presse-papiers et récupérez son contenu sous forme d'URI:

    Kotlin

    // Gets the clipboard data from the clipboard.
    val clip: ClipData? = clipboard.primaryClip
    
    clip?.run {
    
        // Gets the first item from the clipboard data.
        val item: ClipData.Item = getItemAt(0)
    
        // Tries to get the item's contents as a URI.
        val pasteUri: Uri? = item.uri
    

    Java

    // Gets the clipboard data from the clipboard.
    ClipData clip = clipboard.getPrimaryClip();
    
    if (clip != null) {
    
        // Gets the first item from the clipboard data.
        ClipData.Item item = clip.getItemAt(0);
    
        // Tries to get the item's contents as a URI.
        Uri pasteUri = item.getUri();
    
  4. Vérifiez si l'URI est un URI de contenu en appelant getType(Uri) Cette méthode renvoie une valeur nulle si Uri ne pointe pas vers un fournisseur de contenu valide.

    Kotlin

        // If the clipboard contains a URI reference...
        pasteUri?.let {
    
            // ...is this a content URI?
            val uriMimeType: String? = cr.getType(it)
    

    Java

        // If the clipboard contains a URI reference...
        if (pasteUri != null) {
    
            // ...is this a content URI?
            String uriMimeType = cr.getType(pasteUri);
    
  5. Vérifiez si le fournisseur de contenu accepte un type MIME compris par l'application. Si CANNOT TRANSLATE ContentResolver.query() pour obtenir les données. La valeur renvoyée est Cursor

    Kotlin

            // If the return value isn't null, the Uri is a content Uri.
            uriMimeType?.takeIf {
    
                // Does the content provider offer a MIME type that the current
                // application can use?
                it == MIME_TYPE_CONTACT
            }?.apply {
    
                // Get the data from the content provider.
                cr.query(pasteUri, null, null, null, null)?.use { pasteCursor ->
    
                    // If the Cursor contains data, move to the first record.
                    if (pasteCursor.moveToFirst()) {
    
                        // Get the data from the Cursor here.
                        // The code varies according to the format of the data model.
                    }
    
                    // Kotlin `use` automatically closes the Cursor.
                }
            }
        }
    }
    

    Java

            // If the return value isn't null, the Uri is a content Uri.
            if (uriMimeType != null) {
    
                // Does the content provider offer a MIME type that the current
                // application can use?
                if (uriMimeType.equals(MIME_TYPE_CONTACT)) {
    
                    // Get the data from the content provider.
                    Cursor pasteCursor = cr.query(uri, null, null, null, null);
    
                    // If the Cursor contains data, move to the first record.
                    if (pasteCursor != null) {
                        if (pasteCursor.moveToFirst()) {
    
                        // Get the data from the Cursor here.
                        // The code varies according to the format of the data model.
                        }
                    }
    
                    // Close the Cursor.
                    pasteCursor.close();
                 }
             }
         }
    }
    

Coller un intent

Pour coller un intent, commencez par obtenir le presse-papiers global. Examiner l'objet ClipData.Item pour voir s'il contient un élément Intent. Appelez ensuite getIntent() pour copier la dans votre propre espace de stockage. L'extrait de code suivant le démontre :

Kotlin

// Gets a handle to the Clipboard Manager.
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager

// Checks whether the clip item contains an Intent by testing whether
// getIntent() returns null.
val pasteIntent: Intent? = clipboard.primaryClip?.getItemAt(0)?.intent

if (pasteIntent != null) {

    // Handle the Intent.

} else {

    // Ignore the clipboard, or issue an error if
    // you expect an Intent to be on the clipboard.
}

Java

// Gets a handle to the Clipboard Manager.
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);

// Checks whether the clip item contains an Intent, by testing whether
// getIntent() returns null.
Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent();

if (pasteIntent != null) {

    // Handle the Intent.

} else {

    // Ignore the clipboard, or issue an error if
    // you expect an Intent to be on the clipboard.
}

Notification système affichée lorsque votre appli accède aux données du presse-papiers

Sur Android 12 (niveau d'API 31) ou version ultérieure, le système affiche généralement un toast lorsque votre application appels getPrimaryClip() Le texte dans le message se présente sous la forme suivante:

APP pasted from your clipboard

Le système n'affiche pas de toast lorsque votre application effectue l'une des opérations suivantes:

  • Accès ClipData à partir de votre propre application.
  • Accède de manière répétée à ClipData à partir d'une application spécifique. Le toast ne s'affiche que lorsque votre application accède aux données de cette application pour la première fois.
  • Récupère les métadonnées de l'objet clip, par exemple en appelant la méthode getPrimaryClipDescription() au lieu de getPrimaryClip().

Copier des données complexes à l'aide de fournisseurs de contenu

Les fournisseurs de contenu acceptent la copie de données complexes telles que des enregistrements de base de données ou des flux de fichiers. Copier les données, placez un URI de contenu dans le presse-papiers. Après avoir collé des applications, obtenez cet URI presse-papiers et l’utiliser pour récupérer les données de la base de données ou les descripteurs de flux de fichiers.

Comme l'application qui colle ne possède que l'URI de contenu de vos données, elle a besoin de savoir élément de données à récupérer. Vous pouvez fournir ces informations en encodant un identifiant pour les données sur l'URI lui-même, ou vous pouvez fournir un URI unique qui renvoie les données que vous souhaitez copier. Quel que vous choisissez dépend de l'organisation de vos données.

Les sections suivantes décrivent comment configurer des URI, fournir des données complexes et fournir des fichiers flux. Nous partons du principe que vous connaissez les principes généraux conception.

Encoder un identifiant dans l'URI

Une technique utile pour copier des données dans le presse-papiers avec un URI consiste à encoder un identifiant pour les données dans l'URI lui-même. Votre fournisseur de contenu peut alors obtenir l'identifiant à partir de l'URI et utiliser pour récupérer les données. L'application qui colle n'a pas besoin de savoir que l'identifiant existe. Il il suffit d'obtenir votre "référence" (l'URI et l'identifiant) à partir du du presse-papiers, indiquez-lui votre fournisseur de contenu et récupérez les données.

En général, vous encodez un identifiant dans un URI de contenu en le concaténant à la fin de l'URI. Par exemple, supposons que vous définissiez l'URI du fournisseur comme la chaîne suivante:

"content://com.example.contacts"

Si vous souhaitez encoder un nom dans cet URI, utilisez l'extrait de code suivant:

Kotlin

val uriString = "content://com.example.contacts/Smith"

// uriString now contains content://com.example.contacts/Smith.

// Generates a uri object from the string representation.
val copyUri = Uri.parse(uriString)

Java

String uriString = "content://com.example.contacts" + "/" + "Smith";

// uriString now contains content://com.example.contacts/Smith.

// Generates a uri object from the string representation.
Uri copyUri = Uri.parse(uriString);

Si vous utilisez déjà un fournisseur de contenu, vous pouvez ajouter un chemin d'URI indiquant l'URI est destiné à la copie. Par exemple, supposons que vous ayez déjà les chemins d'URI suivants:

"content://com.example.contacts/people"
"content://com.example.contacts/people/detail"
"content://com.example.contacts/people/images"

Vous pouvez ajouter un autre chemin d'accès qui permet de copier les URI:

"content://com.example.contacts/copying"

Vous pouvez alors détecter une "copie" par correspondance de modèle et gérez-le avec un code spécifiques au copier-coller.

Vous utilisez normalement la technique d'encodage si vous avez déjà recours à un fournisseur de contenu, une base de données ou un tableau interne pour organiser vos données. Dans ces cas, vous avez plusieurs éléments de données que vous souhaitez copier, et probablement un identifiant unique pour chaque élément. En réponse à une requête du en collant l'application, vous pouvez rechercher les données à l'aide de leur identifiant et les renvoyer.

Si vous ne disposez pas de plusieurs éléments de données, vous n'avez probablement pas besoin d'encoder un identifiant. Vous pouvez utiliser un URI propre à votre fournisseur. En réponse à une requête, votre fournisseur renvoie données qu'il contient actuellement.

Copier des structures de données

Configurer un fournisseur de contenu pour copier et coller des données complexes en tant que sous-classe de la classe ContentProvider . Encodez l'URI que vous placez dans le presse-papiers afin qu'il pointe vers l'enregistrement exact que vous souhaitez que vous fournissez. En outre, tenez compte de l'état existant de votre application:

  • Si vous disposez déjà d'un fournisseur de contenu, vous pouvez compléter ses fonctionnalités. Vous pourriez seulement modifier sa méthode query() pour gérer les URI provenant d'applications vous souhaitez coller des données. Vous souhaitez probablement modifier la méthode pour gérer une "copie" URI du modèle.
  • Si votre application gère une base de données interne, vous pouvez déplacer cette base de données dans un fournisseur de contenu pour en faciliter la copie.
  • Si vous n'utilisez pas de base de données, vous pouvez mettre en place un fournisseur de contenu simple Le seul but est d'offrir des données aux applications qui collent depuis le presse-papiers.

Dans le fournisseur de contenu, remplacez au moins les méthodes suivantes:

query()
Les applications collées supposent qu'elles peuvent obtenir vos données en utilisant cette méthode avec l'URI que vous mettre dans le presse-papiers. Pour permettre la copie, faites en sorte que cette méthode détecte les URI contenant un mot clé "copier" chemin d'accès. Votre application peut alors créer une "copie" URI à insérer dans la presse-papiers contenant le chemin d'accès à la copie et un pointeur vers l'enregistrement exact que vous souhaitez copier.
getType()
Cette méthode doit renvoyer les types MIME des données que vous souhaitez copier. La méthode newUri() appelle getType() pour placer les types MIME dans la nouvelle ClipData .

Les types MIME pour les données complexes sont décrits dans Fournisseurs de contenu :

Vous n'avez besoin d'aucune des autres méthodes du fournisseur de contenu, insert() ou update() Une application de collage n'a besoin que d'obtenir vos types MIME compatibles et de copier les données de votre fournisseur. Si vous disposez déjà de ces méthodes, elles n'interféreront pas avec les opérations de copie.

Les extraits de code suivants montrent comment configurer votre application pour copier des données complexes:

  1. Dans les constantes globales de votre application, déclarez une chaîne d'URI de base et un chemin d'accès identifie les chaînes d'URI que vous utilisez pour copier des données. Déclarer également un type MIME pour la copie données.

    Kotlin

    // Declares the base URI string.
    private const val CONTACTS = "content://com.example.contacts"
    
    // Declares a path string for URIs that you use to copy data.
    private const val COPY_PATH = "/copy"
    
    // Declares a MIME type for the copied data.
    const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
    

    Java

    // Declares the base URI string.
    private static final String CONTACTS = "content://com.example.contacts";
    
    // Declares a path string for URIs that you use to copy data.
    private static final String COPY_PATH = "/copy";
    
    // Declares a MIME type for the copied data.
    public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
    
  2. Dans l'activité à partir de laquelle les utilisateurs copient les données, configurez le code pour copier les données dans le presse-papiers. En réponse à une requête de copie, placez l'URI dans le presse-papiers.

    Kotlin

    class MyCopyActivity : Activity() {
        ...
    when(item.itemId) {
        R.id.menu_copy -> { // The user has selected a name and is requesting a copy.
            // Appends the last name to the base URI.
            // The name is stored in "lastName".
            uriString = "$CONTACTS$COPY_PATH/$lastName"
    
            // Parses the string into a URI.
            val copyUri: Uri? = Uri.parse(uriString)
    
            // Gets a handle to the clipboard service.
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
            val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
    
            // Sets the clipboard's primary clip.
            clipboard.setPrimaryClip(clip)
        }
    }
    

    Java

    public class MyCopyActivity extends Activity {
        ...
    // The user has selected a name and is requesting a copy.
    case R.id.menu_copy:
    
        // Appends the last name to the base URI.
        // The name is stored in "lastName".
        uriString = CONTACTS + COPY_PATH + "/" + lastName;
    
        // Parses the string into a URI.
        Uri copyUri = Uri.parse(uriString);
    
        // Gets a handle to the clipboard service.
        ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
    
        ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
    
        // Sets the clipboard's primary clip.
        clipboard.setPrimaryClip(clip);
    
  3. Dans le champ d'application global de votre fournisseur de contenu, créez un outil de mise en correspondance des URI et ajoutez un format d'URI correspond aux URI que vous avez placés dans le presse-papiers.

    Kotlin

    // A Uri Match object that simplifies matching content URIs to patterns.
    private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
    
        // Adds a matcher for the content URI. It matches.
        // "content://com.example.contacts/copy/*"
        addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT)
    }
    
    // An integer to use in switching based on the incoming URI pattern.
    private const val GET_SINGLE_CONTACT = 0
    ...
    class MyCopyProvider : ContentProvider() {
        ...
    }
    

    Java

    public class MyCopyProvider extends ContentProvider {
        ...
    // A Uri Match object that simplifies matching content URIs to patterns.
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    
    // An integer to use in switching based on the incoming URI pattern.
    private static final int GET_SINGLE_CONTACT = 0;
    ...
    // Adds a matcher for the content URI. It matches
    // "content://com.example.contacts/copy/*"
    sUriMatcher.addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT);
    
  4. Configurez le query() . Cette méthode peut gérer différents formats d'URI, selon la façon dont vous la codez, mais seulement le schéma de l'opération de copie du presse-papiers s'affiche.

    Kotlin

    // Sets up your provider's query() method.
    override fun query(
            uri: Uri,
            projection: Array<out String>?,
            selection: String?,
            selectionArgs: Array<out String>?,
            sortOrder: String?
    ): Cursor? {
        ...
        // When based on the incoming content URI:
        when(sUriMatcher.match(uri)) {
    
            GET_SINGLE_CONTACT -> {
    
                // Queries and returns the contact for the requested name. Decodes
                // the incoming URI, queries the data model based on the last name,
                // and returns the result as a Cursor.
            }
        }
        ...
    }
    

    Java

    // Sets up your provider's query() method.
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
        String sortOrder) {
        ...
        // Switch based on the incoming content URI.
        switch (sUriMatcher.match(uri)) {
    
        case GET_SINGLE_CONTACT:
    
            // Queries and returns the contact for the requested name. Decodes the
            // incoming URI, queries the data model based on the last name, and
            // returns the result as a Cursor.
        ...
    }
    
  5. Configurez la méthode getType() afin de renvoyer un type MIME approprié pour les copies données:

    Kotlin

    // Sets up your provider's getType() method.
    override fun getType(uri: Uri): String? {
        ...
        return when(sUriMatcher.match(uri)) {
            GET_SINGLE_CONTACT -> MIME_TYPE_CONTACT
            ...
        }
    }
    

    Java

    // Sets up your provider's getType() method.
    public String getType(Uri uri) {
        ...
        switch (sUriMatcher.match(uri)) {
        case GET_SINGLE_CONTACT:
            return (MIME_TYPE_CONTACT);
        ...
        }
    }
    

La section Coller les données à partir d'un URI de contenu explique comment obtenir une dans le presse-papiers et l'utiliser pour obtenir et coller des données.

Copier des flux de données

Vous pouvez copier et coller de grandes quantités de texte et de données binaires sous forme de flux. Les données peuvent avoir des formulaires par exemple:

  • Fichiers stockés sur l'appareil
  • Flux provenant de sockets
  • De grandes quantités de données stockées dans le système de base de données sous-jacent d’un fournisseur

Un fournisseur de contenu pour des flux de données fournit un accès à ses données à l'aide d'un objet descripteur de fichier. tels que AssetFileDescriptor, au lieu d'un objet Cursor. L'application de collage lit le flux de données à l'aide de ce descripteur de fichier.

Pour configurer votre application afin de copier un flux de données avec un fournisseur, procédez comme suit:

  1. Configurez un URI de contenu pour le flux de données que vous placez dans le presse-papiers. Options pour cela sont les suivantes: <ph type="x-smartling-placeholder">
      </ph>
    • Encodez un identifiant pour le flux de données dans l'URI, comme décrit dans les Encodez un identifiant dans la section "URI", puis gérez une de votre fournisseur contenant les identifiants et le nom de flux correspondant.
    • Encodez le nom du flux directement dans l'URI.
    • Utilisez un URI unique qui renvoie toujours le flux actuel du fournisseur. Si vous utilisez cette option, n'oubliez pas de modifier votre fournisseur pour qu'il pointe vers un autre flux chaque fois que vous copiez le flux dans le presse-papiers à l'aide de l'URI.
  2. Indiquez un type MIME pour chaque type de flux de données que vous prévoyez de proposer. Collage des applications... ont besoin de ces informations pour déterminer s'ils peuvent coller les données dans le presse-papiers.
  3. Implémentez l'une des méthodes ContentProvider qui renvoie un descripteur de fichier pour un flux. Si vous encodez des identifiants dans l'URI de contenu, cette méthode vous permet de déterminer flux pour ouvrir.
  4. Pour copier le flux de données dans le presse-papiers, créez l'URI de contenu et placez-le sur le presse-papiers.

Pour coller un flux de données, une application extrait l'extrait du presse-papiers, obtient l'URI et utilise dans un appel à une méthode de descripteur de fichier ContentResolver qui ouvre le flux. La La méthode ContentResolver appelle la méthode ContentProvider correspondante, en lui transmettant l'URI de contenu. Votre fournisseur renvoie le descripteur de fichier ContentResolver. L'application de collage a alors la responsabilité de lire les données du flux.

La liste suivante répertorie les méthodes de descripteur de fichier les plus importantes pour un fournisseur de contenu. Chaque possède une méthode ContentResolver correspondante avec la chaîne « Descripteur » : au nom de la méthode. Par exemple, ContentResolver analogue de openAssetFile() correspond à openAssetFileDescriptor()

openTypedAssetFile()

Cette méthode renvoie un descripteur de fichier d'éléments, mais seulement si le type MIME fourni est pris en charge par le fournisseur. L'appelant (l'application effectuant le collage) fournit un format de type MIME. Le fournisseur de contenu de l'application qui copie un URI le presse-papiers renvoie un handle de fichier AssetFileDescriptor s'il peut fournir que type MIME et génère une exception si ce n'est pas possible.

Cette méthode gère les sous-sections de fichiers. Vous pouvez l'utiliser pour lire les éléments Le fournisseur de contenu a copié dans le presse-papiers.

openAssetFile()
Cette méthode est une forme plus générale de openTypedAssetFile(). Il ne filtre pas pour les types MIME autorisés, mais il peut lire des sous-sections des fichiers.
openFile()
Il s'agit d'une forme plus générale de openAssetFile(). Il ne peut pas lire les sous-sections de .

Vous pouvez éventuellement utiliser le openPipeHelper() par votre méthode de descripteur de fichier. Cela permet à l'application de collage de lire les données de flux dans un thread d'arrière-plan à l'aide d'un pipe. Pour utiliser cette méthode, implémentez la ContentProvider.PipeDataWriter de commande.

Concevoir une fonctionnalité de copier-coller efficace

Pour concevoir une fonctionnalité de copier-coller efficace pour votre application, tenez compte des points suivants:

  • À tout moment, il n'y a qu'un seul extrait dans le presse-papiers. Une nouvelle opération de copie l'application du système écrase l'extrait précédent. Puisque l'utilisateur peut naviguer et de le copier avant de le renvoyer, vous ne pouvez pas supposer que le presse-papiers contient l'extrait que l'utilisateur a copié dans votre application.
  • L'objectif de plusieurs objets ClipData.Item par extrait est prennent en charge le copier-coller de plusieurs sélections plutôt que de différentes formes de à une seule sélection. En général, vous avez besoin de toutes les ClipData.Item objets d'un clip ont la même forme. Autrement dit, il doit s'agir de simples textes, de contenus URI ou Intent, et non mixte.
  • Lorsque vous fournissez des données, vous pouvez proposer différentes représentations MIME. Ajouter les types MIME que vous prenez en charge ClipDescription, puis implémentez les types MIME dans votre fournisseur de contenu.
  • Lorsque vous récupérez des données du presse-papiers, votre application est chargée de vérifier le les types MIME disponibles, puis de décider quel type MIME utiliser, le cas échéant. Même s'il y a un dans le presse-papiers et que l'utilisateur demande un collage, votre application n'est pas requise pour effectuer le collage. Procédez au collage si le type MIME est compatible. Vous pouvez forcer les données du presse-papiers en texte à l'aide de coerceToText(). Si votre application est compatible avec plusieurs types MIME disponibles, vous pouvez laisser l'utilisateur choisir lequel utiliser.