Android fournit un framework puissant basé sur le presse-papiers pour la copie et le collage. Il est compatible avec les types de données simples et complexes, y compris les chaînes de texte, les structures de données complexes, les données de flux de texte et binaires, ainsi que les éléments d'application. Les données textuelles simples sont stockées directement dans le presse-papiers, tandis que les données complexes sont stockées en tant que référence que l'application de collage résout avec un fournisseur de contenu. Le copier-coller fonctionne à la fois dans une application et entre les applications qui implémentent le framework.
Étant donné qu'une partie du framework utilise des fournisseurs de contenu, ce document suppose une certaine connaissance de l'API du fournisseur de contenu Android, décrite dans la section Fournisseurs de contenu.
Les utilisateurs s'attendent à recevoir des commentaires lorsqu'ils copient du contenu dans le presse-papiers. Par conséquent, en plus du framework qui permet de copier et coller, Android affiche une interface utilisateur par défaut aux utilisateurs lors de la copie sous Android 13 (niveau d'API 33) ou version ultérieure. En raison de cette fonctionnalité, il existe un risque de notification en double. Pour en savoir plus sur ce cas particulier, consultez la section Éviter les notifications en double.
Fournissez manuellement des commentaires aux utilisateurs lors de la copie sous Android 12L (niveau d'API 32) ou version antérieure. Consultez les recommandations à ce sujet dans ce document.
Structure du presse-papiers
Lorsque vous utilisez le framework de presse-papiers, placez les données dans un objet de presse-papiers, puis placez l'objet de presse-papiers dans le presse-papiers à l'échelle du système. L'objet extrait peut prendre l'une des trois formes suivantes:
- Texte
- Une chaîne de texte. Placez la chaîne directement dans l'objet clip, que vous placez ensuite dans le presse-papiers. Pour coller la chaîne, récupérez l'objet clip dans le presse-papiers, puis copiez la chaîne dans l'espace de stockage de votre application.
- URI
-
Objet
Uri
représentant toute forme d'URI. Cette option est principalement utilisée pour copier des données complexes à partir d'un fournisseur de contenu. Pour copier des données, placez un objetUri
dans un objet clip et placez l'objet clip dans le presse-papiers. Pour coller les données, récupérez l'objet extrait et l'objetUri
, associez-le à une source de données, telle qu'un fournisseur de contenu, puis copiez les données de la source dans l'espace de stockage de votre application. - Intent
-
Un
Intent
. Cela permet de copier des raccourcis d'application. Pour copier des données, créez unIntent
, placez-le dans un objet clip, puis placez l'objet clip dans le presse-papiers. Pour coller les données, récupérez l'objet clip, puis copiez l'objetIntent
dans la zone de mémoire de votre application.
Le presse-papiers ne contient qu'un élément clip à la fois. Lorsqu'une application place un objet extrait dans le presse-papiers, l'objet extrait précédent disparaît.
Si vous souhaitez autoriser les utilisateurs à coller des données dans votre application, vous n'avez pas besoin de gérer tous les types de données. Vous pouvez examiner les données dans le presse-papiers avant de permettre aux utilisateurs de les coller. En plus d'avoir une certaine forme de données, l'objet extrait contient également des métadonnées qui vous indiquent les types MIME disponibles. Ces métadonnées vous aident à décider si votre application peut effectuer une action utile avec les données du presse-papiers. Par exemple, si votre application gère principalement du texte, vous pouvez ignorer les objets rogner 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. Pour ce faire, forcez les données du presse-papiers à être représentées sous forme de texte, puis collez ce texte. Ce processus est décrit dans la section Forcer le presse-papiers en texte.
Classes de presse-papiers
Cette section décrit les classes utilisées par le framework de presse-papiers.
ClipboardManager
Le presse-papiers du système Android est représenté par la classe ClipboardManager
globale.
N'instanciez pas directement cette classe. Obtenez plutôt une référence à celle-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 les données elles-mêmes. Le presse-papiers ne contient qu'un seul ClipData
à la fois. 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 du texte stylisé et le type de texte de l'objet.
Lorsque vous placez un extrait dans le presse-papiers, ces informations sont disponibles pour les applications de collage, qui peuvent vérifier si elles peuvent gérer les données de l'extrait.
Un objet ClipData.Item
contient le texte, l'URI ou les données d'intent:
- Texte
-
A
CharSequence
. - URI
-
Une
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. Les applications qui souhaitent coller les données obtiennent l'URI à partir du presse-papiers et l'utilisent pour accéder au fournisseur de contenu ou à une autre source de données, puis pour récupérer les données. - Intent
-
Un
Intent
. Ce type de données vous permet de copier un raccourci d'application dans le presse-papiers. Les utilisateurs peuvent ensuite coller le raccourci dans leurs applications pour l'utiliser plus tard.
Vous pouvez ajouter plusieurs objets ClipData.Item
à un extrait. Cela permet aux utilisateurs de copier et coller plusieurs sélections en un seul extrait. Par exemple, si vous disposez d'un widget de liste qui permet à l'utilisateur de sélectionner plusieurs éléments à la fois, vous pouvez copier tous les éléments dans le presse-papiers en même temps. Pour ce faire, créez un ClipData.Item
distinct pour chaque élément de la liste, puis ajoutez les objets ClipData.Item
à l'objet ClipData
.
Méthodes de base ClipData
La classe ClipData
fournit des méthodes pratiques statiques pour créer un objet ClipData
avec un seul objet ClipData.Item
et un objet ClipDescription
simple:
-
newPlainText(label, text)
- Renvoie un objet
ClipData
dont l'objetClipData.Item
unique contient une chaîne de texte. Le libellé de l'objetClipDescription
est défini surlabel
. Le seul type MIME dansClipDescription
estMIMETYPE_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'objetClipData.Item
unique contient un URI. Le libellé de l'objetClipDescription
est défini surlabel
. Si l'URI est un URI de contenu (c'est-à-dire siUri.getScheme()
renvoiecontent:
), la méthode utilise l'objetContentResolver
fourni dansresolver
pour récupérer les types MIME disponibles auprès du fournisseur de contenu. Il les stocke ensuite dansClipDescription
. Pour un URI qui n'est pas un URIcontent:
, la méthode définit le type MIME surMIMETYPE_TEXT_URILIST
.Utilisez
newUri()
pour créer un extrait à partir d'un URI, en particulier d'un URIcontent:
. -
newIntent(label, intent)
- Renvoie un objet
ClipData
dont l'unique objetClipData.Item
contient unIntent
. Le libellé de l'objetClipDescription
est défini surlabel
. Le type MIME est défini surMIMETYPE_TEXT_INTENT
.Utilisez
newIntent()
pour créer un extrait à partir d'un objetIntent
.
Forcer les données du presse-papiers en texte
Même si votre application ne gère que du texte, vous pouvez copier des données non textuelles à partir du presse-papiers en les convertissant à l'aide de la méthode ClipData.Item.coerceToText()
.
Cette méthode convertit les données de ClipData.Item
en texte et renvoie un CharSequence
. La valeur renvoyée par ClipData.Item.coerceToText()
dépend de la forme des données dans ClipData.Item
:
- Texte
-
Si
ClipData.Item
est du texte, c'est-à-dire sigetText()
n'est pas nul, coerceToText() renvoie le texte. - URI
- Si
ClipData.Item
est un URI, c'est-à-dire sigetUri()
n'est pas nul,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 n'offre pas de flux de texte,
coerceToText()
renvoie une représentation de l'URI. La représentation est identique à celle renvoyée parUri.toString()
. - Si l'URI n'est pas un URI de contenu,
coerceToText()
renvoie une représentation de l'URI. La représentation est identique à celle renvoyée parUri.toString()
.
- Si l'URI est un URI de contenu et que le fournisseur peut renvoyer un flux de texte,
- Intent
- Si
ClipData.Item
est unIntent
(c'est-à-dire sigetIntent()
n'est pas nul),coerceToText()
le convertit en URI d'intent et le renvoie. La représentation est identique à celle renvoyée parIntent.toUri(URI_INTENT_SCHEME)
.
Le framework du presse-papiers est résumé dans la figure 2. Pour copier des données, une application place un objet ClipData
dans le presse-papiers global ClipboardManager
. ClipData
contient un ou plusieurs objets ClipData.Item
et un objet ClipDescription
. Pour coller des données, une application obtient le ClipData
, obtient son type MIME à partir du ClipDescription
et obtient les données à partir du ClipData.Item
ou du fournisseur de contenu référencé par ClipData.Item
.
Copier dans le presse-papiers
Pour copier des données dans le presse-papiers, obtenez un gestionnaire de l'objet ClipboardManager
global, créez un objet ClipData
, puis ajoutez un objet ClipDescription
et un ou plusieurs objets ClipData.Item
. Ajoutez ensuite l'objet ClipData
finalisé à l'objet ClipboardManager
. Ce processus est décrit plus en détail dans la procédure suivante:
- Si vous copiez des données à l'aide d'un URI de contenu, configurez un fournisseur de contenu.
- Obtenez le presse-papiers 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);
-
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 de code crée un URI en encodant un ID d'enregistrement sur l'URI de contenu du fournisseur. Cette technique est abordée plus en détail dans la section Encodage d'un identifiant dans l'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 le 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);
-
Texte
-
Placez le nouvel objet de 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. Cette opération est effectuée automatiquement pour les utilisateurs d'Android 13 ou version ultérieure, mais elle doit être implémentée manuellement dans les versions antérieures.
À partir d'Android 13, le système affiche une confirmation visuelle standard lorsque du contenu est ajouté au presse-papiers. La nouvelle confirmation effectue les opérations suivantes:
- confirme que le contenu a bien été copié.
- Affiche un aperçu du contenu copié.
Sous Android 12L (niveau d'API 32) ou version antérieure, les utilisateurs peuvent ne pas savoir s'ils ont bien copié du contenu ni ce qu'ils ont copié. Cette fonctionnalité standardise les différentes notifications affichées par les applications après la copie et offre aux utilisateurs plus de contrôle sur le presse-papiers.
Éviter les notifications en double
Sur Android 12L (niveau d'API 32) et versions antérieures, nous vous recommandons d'alerter les utilisateurs lorsqu'ils effectuent une copie réussie en envoyant des commentaires visuels dans l'application, à l'aide d'un widget comme Toast
ou Snackbar
, après la copie.
Pour éviter de dupliquer les informations, nous vous recommandons vivement de supprimer les toasts ou les barres de notification affichés après une copie dans l'application pour Android 13 et versions ultérieures.
Voici un exemple d'implémentation:
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 au presse-papiers
Si votre application permet aux utilisateurs de copier du contenu sensible dans le presse-papiers, comme des mots de passe ou des informations de carte de crédit, vous devez ajouter un indicateur à ClipDescription
dans ClipData
avant d'appeler ClipboardManager.setPrimaryClip()
. L'ajout de cet indicateur empêche l'affichage de contenu sensible dans la confirmation visuelle du contenu copié sous Android 13 ou version ultérieure.
Pour signaler un contenu sensible, ajoutez un élément booléen supplémentaire à ClipDescription
. Toutes les applications 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 presse-papiers global, en obtenant l'objet clip, en examinant ses données et, si possible, en copiant les données de l'objet clip vers votre propre stockage. Cette section explique en détail comment coller les trois formes de données du presse-papiers.
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. Obtenez ensuite l'objet extrait et copiez son texte dans votre propre espace de stockage à l'aide de getText()
, comme décrit dans la procédure suivante:
- Obtenez l'objet
ClipboardManager
global à l'aide degetSystemService(CLIPBOARD_SERVICE)
. Déclarez également une variable globale pour contenir 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 = "";
- Déterminez si vous devez activer ou désactiver l'option "Coller" dans l'activité en cours. Vérifiez que le presse-papiers contient un extrait et que vous pouvez gérer le type de données représenté 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); }
- Copiez les données depuis le presse-papiers. Ce point du code n'est accessible que si l'élément de menu "Coller" est activé. Vous pouvez donc supposer que le presse-papiers contient du texte brut. 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 il 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 des données à partir d'un URI de contenu
Si l'objet ClipData.Item
contient un URI de contenu et que vous déterminez que vous pouvez gérer l'un de ses types MIME, créez un ContentResolver
et appelez la méthode de fournisseur de contenu appropriée pour récupérer les données.
La procédure suivante décrit comment obtenir des données auprès d'un fournisseur de contenu en fonction d'un URI de contenu dans le presse-papiers. Il vérifie si un type MIME que l'application peut utiliser est disponible auprès du fournisseur.
-
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";
- Obtenir le presse-papiers global Obtenez également un résolveur de contenu pour pouvoir accéder au fournisseur de contenu:
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();
- Obtenez le clip principal du presse-papiers et 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();
- Vérifiez si l'URI est un URI de contenu en appelant
getType(Uri)
. Cette méthode renvoie la valeur "null" siUri
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);
- Vérifiez si le fournisseur de contenu accepte un type MIME compris par l'application. Si c'est le cas, appelez
ContentResolver.query()
pour obtenir les données. La valeur renvoyée est unCursor
.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, obtenez d'abord le presse-papiers global. Examinez l'objet ClipData.Item
pour vérifier s'il contient un élément Intent
. Appelez ensuite getIntent()
pour copier l'intent 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 application 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 message de type toast lorsque votre application appelle 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 message toast lorsque votre application effectue l'une des opérations suivantes:
- Accède à
ClipData
à partir de votre propre application. - Accédez de manière répétée à
ClipData
à partir d'une application spécifique. Le message 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 extrait, par exemple en appelant
getPrimaryClipDescription()
au lieu degetPrimaryClip()
.
Utiliser des fournisseurs de contenu pour copier des données complexes
Les fournisseurs de contenu permettent de copier des données complexes telles que des enregistrements de base de données ou des flux de fichiers. Pour copier les données, placez un URI de contenu dans le presse-papiers. Les applications de collage récupèrent ensuite cet URI dans le presse-papiers et l'utilisent pour récupérer les données de base de données ou les descripteurs de flux de fichiers.
Étant donné que l'application qui colle ne possède que l'URI de contenu de vos données, elle doit savoir quel élément de données récupérer. Vous pouvez fournir ces informations en encodant un identifiant pour les données dans l'URI lui-même, ou vous pouvez fournir un URI unique qui renvoie les données que vous souhaitez copier. La technique à choisir dépend de l'organisation de vos données.
Les sections suivantes expliquent comment configurer des URI, fournir des données complexes et fournir des flux de fichiers. Les descriptions partent du principe que vous connaissez les principes généraux de conception des fournisseurs de contenu.
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 sur l'URI lui-même. Votre fournisseur de contenu peut ensuite obtenir l'identifiant à partir de l'URI et l'utiliser pour récupérer les données. L'application de collage n'a pas besoin de savoir que l'identifiant existe. Il lui suffit d'obtenir votre "référence" (l'URI plus l'identifiant) à partir du presse-papiers, de lui fournir votre fournisseur de contenu et de récupérer les données.
Vous encodez généralement un identifiant sur un URI de contenu en le concatenant à la fin de l'URI. Par exemple, supposons que vous définissiez l'URI de votre fournisseur comme suit:
"content://com.example.contacts"
Si vous souhaitez encoder un nom sur 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 nouveau chemin d'URI indiquant que l'URI est à des fins de copie. Par exemple, supposons que vous disposiez déjà des 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 pour copier les URI:
"content://com.example.contacts/copying"
Vous pouvez ensuite détecter un URI de "copie" par mise en correspondance de modèles et le gérer avec du code spécifique à la copie et au collage.
Vous utilisez généralement la technique d'encodage si vous utilisez déjà un fournisseur de contenu, une base de données interne ou une table interne pour organiser vos données. Dans ce cas, vous souhaitez copier plusieurs éléments de données, et probablement un identifiant unique pour chacun d'eux. En réponse à une requête de l'application de collage, 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 d'identifiant. Vous pouvez utiliser un URI propre à votre fournisseur. En réponse à une requête, votre fournisseur renvoie les données qu'il contient actuellement.
Copier des structures de données
Configurez un fournisseur de contenu pour copier et coller des données complexes en tant que sous-classe du composant ContentProvider
. Encodez l'URI que vous mettez dans le presse-papiers afin qu'il pointe vers l'enregistrement exact que vous souhaitez fournir. En outre, tenez compte de l'état actuel de votre application:
- Si vous disposez déjà d'un fournisseur de contenu, vous pouvez en améliorer les fonctionnalités. Vous devrez peut-être uniquement modifier sa méthode
query()
pour gérer les URI provenant d'applications qui souhaitent coller des données. Vous devrez probablement modifier la méthode pour gérer un modèle d'URI de copie. - Si votre application gère une base de données interne, vous pouvez la déplacer vers un fournisseur de contenu pour faciliter la copie à partir de celle-ci.
- Si vous n'utilisez pas de base de données, vous pouvez mettre en œuvre un fournisseur de contenu simple dont le seul objectif est d'offrir des données aux applications collées depuis le presse-papiers.
Dans le fournisseur de contenu, remplacez au moins les méthodes suivantes:
-
query()
- Les applications de collage supposent qu'elles peuvent obtenir vos données à l'aide de cette méthode avec l'URI que vous avez placé dans le presse-papiers. Pour permettre la copie, demandez à cette méthode de détecter les URI contenant un chemin d'accès "copy" spécial. Votre application peut ensuite créer un URI de "copie" à placer dans le presse-papiers, contenant le chemin d'accès de 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()
appellegetType()
pour placer les types MIME dans le nouvel objetClipData
.Les types MIME pour les données complexes sont décrits dans la section Fournisseurs de contenu.
Vous n'avez pas besoin d'utiliser les autres méthodes de fournisseur de contenu, telles que 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:
-
Dans les constantes globales de votre application, déclarez une chaîne d'URI de base et un chemin d'accès qui identifie les chaînes d'URI que vous utilisez pour copier des données. Déclarez également un type MIME pour les données copié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";
- Dans l'activité à partir de laquelle les utilisateurs copient des données, configurez le code pour qu'il copie 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);
-
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 correspondant aux URI que vous placez 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);
-
Configurez la méthode
query()
. Cette méthode peut gérer différents modèles d'URI, en fonction de la manière dont vous la codez, mais seul le modèle de l'opération de copie dans le 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. ... }
-
Configurez la méthode
getType()
pour qu'elle renvoie un type MIME approprié pour les données copié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 des données à partir d'un URI de contenu décrit comment obtenir un URI de contenu à partir du 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 prendre les formes suivantes:
- Fichiers stockés sur l'appareil
- Flux à partir 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, tel 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:
-
Configurez un URI de contenu pour le flux de données que vous placez dans le presse-papiers. Vous pouvez par exemple :
- Encodez un identifiant pour le flux de données dans l'URI, comme décrit dans la section Encoder un identifiant dans l'URI, puis gérez une table au niveau de votre fournisseur contenant les identifiants et le nom du 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 mettre à jour 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.
- Indiquez un type MIME pour chaque type de flux de données que vous prévoyez d'offrir. Les applications de collage ont besoin de ces informations pour déterminer si elles peuvent coller les données dans le presse-papiers.
- Implémentez l'une des méthodes
ContentProvider
qui renvoie un descripteur de fichier pour un flux. Si vous encodez des identifiants sur l'URI du contenu, utilisez cette méthode pour déterminer le flux à ouvrir. - Pour copier le flux de données dans le presse-papiers, créez l'URI de contenu et placez-le dans le presse-papiers.
Pour coller un flux de données, une application récupère le clip dans le presse-papiers, obtient l'URI et l'utilise dans un appel à une méthode de descripteur de fichier ContentResolver
qui ouvre le flux. La méthode ContentResolver
appelle la méthode ContentProvider
correspondante, en lui transmettant l'URI de contenu. Votre fournisseur renvoie le descripteur de fichier à la méthode ContentResolver
. L'application de collage est alors chargée de lire les données du flux.
La liste suivante présente les méthodes de descripteur de fichier les plus importantes pour un fournisseur de contenu. Chacun d'eux est associé à une méthode ContentResolver
avec la chaîne "Descriptor" ajoutée au nom de la méthode. Par exemple, l'équivalent ContentResolver
de openAssetFile()
est openAssetFileDescriptor()
.
-
openTypedAssetFile()
-
Cette méthode renvoie un descripteur de fichier d'élément, mais uniquement si le type MIME fourni est compatible avec le fournisseur. L'appelant (l'application effectuant le collage) fournit un modèle de type MIME. Le fournisseur de contenu de l'application qui copie un URI dans le presse-papiers renvoie un gestionnaire de fichiers
AssetFileDescriptor
s'il peut fournir ce type MIME et génère une exception s'il ne peut pas le faire.Cette méthode gère les sous-sections de fichiers. Vous pouvez l'utiliser pour lire les éléments que le fournisseur de contenu a copiés dans le presse-papiers.
-
openAssetFile()
-
Cette méthode est une forme plus générale de
openTypedAssetFile()
. Il ne filtre pas les types MIME autorisés, mais il peut lire des sous-sections de fichiers. -
openFile()
-
Il s'agit d'une forme plus générale de
openAssetFile()
. Il ne peut pas lire les sous-sections des fichiers.
Vous pouvez éventuellement utiliser la méthode openPipeHelper()
avec votre méthode de descripteur de fichier. Cela permet à l'application de collage de lire les données du flux dans un thread en arrière-plan à l'aide d'un canal. Pour utiliser cette méthode, implémentez l'interface ContentProvider.PipeDataWriter
.
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 effectuée par une application du système écrase l'extrait précédent. Étant donné que l'utilisateur peut quitter votre application et copier du contenu avant de revenir, vous ne pouvez pas supposer que le presse-papiers contient le contenu que l'utilisateur a précédemment copié dans votre application.
-
L'objectif de plusieurs objets
ClipData.Item
par extrait est de permettre le copier-coller de plusieurs sélections plutôt que de prendre en charge différentes formes de références à une seule sélection. En général, vous souhaitez que tous les objetsClipData.Item
d'un extrait aient la même forme. Autrement dit, ils doivent tous être du texte simple, des URI de contenu ou desIntent
, et non mélangés. -
Lorsque vous fournissez des données, vous pouvez proposer différentes représentations MIME. Ajoutez les types MIME que vous prenez en charge à
ClipDescription
, puis implémentez-les dans votre fournisseur de contenu. -
Lorsque vous récupérez des données à partir du presse-papiers, votre application est chargée de vérifier les types MIME disponibles, puis de décider de celui à utiliser, le cas échéant. Même si un élément est copié dans le presse-papiers et que l'utilisateur demande à le coller, votre application n'est pas obligée de le faire. Effectuez la copie si le type MIME est compatible. Vous pouvez convertir 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 celui à utiliser.