Android fornisce un potente framework basato sulla clipboard per copiare e incollare. Supporta tipi di dati semplici e complessi, tra cui stringhe di testo, strutture di dati complesse, dati di stream di testo e binari e asset dell'applicazione. I dati di testo semplici vengono memorizzati direttamente negli appunti, mentre i dati complessi vengono memorizzati come riferimento risolto dall'applicazione di incollaggio con un fornitore di contenuti. La copia e l'incollaggio funzionano sia all'interno di un'applicazione sia tra applicazioni che implementano il framework.
Poiché parte del framework utilizza i fornitori di contenuti, questo documento presuppone una certa familiarità con l'API Android Content Provider, descritta in Fornitori di contenuti.
Gli utenti si aspettano un feedback quando copiano i contenuti nella clipboard, quindi, oltre al framework che supporta la funzionalità di copia e incolla, Android mostra agli utenti un'interfaccia utente predefinita durante la copia in Android 13 (livello API 33) e versioni successive. A causa di questa funzionalità, esiste il rischio di una notifica duplicata. Scopri di più su questo caso limite nella sezione Evitare notifiche duplicate.
Fornire manualmente un feedback agli utenti durante la copia in Android 12L (livello API 32) e versioni precedenti. Consulta i consigli a riguardo in questo documento.
Il framework degli appunti
Quando utilizzi il framework della clipboard, inserisci i dati in un oggetto clip e poi inserisci l'oggetto clip nella clipboard di sistema. L'oggetto clip può assumere una di tre forme:
- Testo
- Una stringa di testo. Inserisci la stringa direttamente nell'oggetto clip, che poi inserisci nella clipboard. Per incollare la stringa, recupera l'oggetto clip dagli appunti e copia la stringa nello spazio di archiviazione dell'applicazione.
- URI
-
Un oggetto
Uri
che rappresenta qualsiasi tipo di URI. Questo metodo è principalmente destinato alla copia di dati complessi da un fornitore di contenuti. Per copiare i dati, inserisci un oggettoUri
in un oggetto clip e inserisci l'oggetto clip negli appunti. Per incollare i dati, recupera l'oggetto clip, recupera l'oggettoUri
, risolvilo in un'origine dati, ad esempio un fornitore di contenuti, e copia i dati dall'origine nello spazio di archiviazione dell'applicazione. - Intenzione
-
Un
Intent
. Questo supporta la copia delle scorciatoie delle applicazioni. Per copiare i dati, crea unIntent
, inseriscilo in un oggetto clip e inserisci l'oggetto clip negli appunti. Per incollare i dati, recupera l'oggetto clip, quindi copia l'oggettoIntent
nell'area di memoria dell'applicazione.
La clipboard contiene un solo oggetto clip alla volta. Quando un'applicazione inserisce un oggetto clip nella clipboard, l'oggetto clip precedente scompare.
Se vuoi consentire agli utenti di incollare dati nella tua applicazione, non devi gestire tutti i tipi di dati. Puoi esaminare i dati negli appunti prima di offrire agli utenti l'opzione per incollarli. Oltre ad avere un determinato formato di dati, l'oggetto clip contiene anche metadati che indicano i tipi MIME disponibili. Questi metadati ti aiutano a decidere se la tua applicazione può fare qualcosa di utile con i dati della clipboard. Ad esempio, se hai un'applicazione che gestisce principalmente il testo, potresti ignorare gli oggetti clip che contengono un URI o un'intenzione.
Potresti anche consentire agli utenti di incollare il testo indipendentemente dalla forma dei dati nella clipboard. Per farlo, forza i dati degli appunti in una rappresentazione di testo, quindi incolla questo testo. Questo è описано в разделе Costringere gli appunti a essere in formato di testo.
Classi di appunti
Questa sezione descrive le classi utilizzate dal framework della clipboard.
ClipboardManager
Gli appunti del sistema Android sono rappresentati dalla classe ClipboardManager
globale.
Non creare un'istanza di questa classe direttamente. Recupera invece un riferimento richiamando
getSystemService(CLIPBOARD_SERVICE)
.
ClipData, ClipData.Item e ClipDescription
Per aggiungere dati agli appunti, crea un oggetto ClipData
che contenga una descrizione dei dati e dei dati stessi. Gli appunti contengono un ClipData
alla volta. Un ClipData
contiene un oggetto ClipDescription
e uno o più oggetti ClipData.Item
.
Un oggetto ClipDescription
contiene metadati relativi al clip. In particolare, contiene un array di tipi MIME disponibili per i dati del clip. Inoltre, su Android 12 (livello API 31) e versioni successive, i metadati includono informazioni sull'eventuale presenza di testo stilizzato nell'oggetto e sul tipo di testo nell'oggetto.
Quando inserisci un clip negli appunti, queste informazioni sono disponibili per le applicazioni di incollaggio, che possono esaminare se sono in grado di gestire i dati del clip.
Un oggetto ClipData.Item
contiene i dati di testo, URI o intent:
- Testo
-
Un
CharSequence
. - URI
-
Un
Uri
. Di solito contiene un URI del fornitore di contenuti, anche se è consentito qualsiasi URI. L'applicazione che fornisce i dati inserisce l'URI negli appunti. Le applicazioni che vogliono incollare i dati recuperano l'URI dagli appunti e lo utilizzano per accedere al fornitore di contenuti o a un'altra origine dati e recuperare i dati. - Intent
-
Un
Intent
. Questo tipo di dati consente di copiare una scorciatoia dell'applicazione negli appunti. Gli utenti possono quindi incollare la scorciatoia nelle loro applicazioni per utilizzarla in un secondo momento.
Puoi aggiungere più di un oggetto ClipData.Item
a un clip. In questo modo, gli utenti possono copiare e incollare più selezioni come un unico clip. Ad esempio, se disponi di un widget elenco che consente all'utente di selezionare più elementi alla volta, puoi copiare tutti gli elementi negli appunti contemporaneamente. Per farlo, crea un ClipData.Item
separato per ogni elemento dell'elenco, quindi aggiungi gli oggetti ClipData.Item
all'oggetto ClipData
.
Metodi di utilità di ClipData
La classe ClipData
fornisce metodi di utilità statici per creare un oggetto
ClipData
con un singolo oggetto ClipData.Item
e un semplice
ClipDescription
:
-
newPlainText(label, text)
- Restituisce un oggetto
ClipData
il cui singolo oggettoClipData.Item
contiene una stringa di testo. L'etichetta dell'oggettoClipDescription
è impostata sulabel
. Il singolo tipo MIME inClipDescription
èMIMETYPE_TEXT_PLAIN
.Usa
newPlainText()
per creare un clip da una stringa di testo. -
newUri(resolver, label, URI)
- Restituisce un oggetto
ClipData
il cui singolo oggettoClipData.Item
contiene un URI. L'etichetta dell'oggettoClipDescription
è impostata sulabel
. Se l'URI è un URI dei contenuti, ovvero seUri.getScheme()
restituiscecontent:
, il metodo utilizza l'oggettoContentResolver
fornito inresolver
per recuperare i tipi MIME disponibili dal fornitore di contenuti. Poi le memorizza inClipDescription
. Per un URI che non è un URIcontent:
, il metodo imposta il tipo MIME suMIMETYPE_TEXT_URILIST
.Utilizza
newUri()
per creare un clip da un URI, in particolare da un URIcontent:
. -
newIntent(label, intent)
- Restituisce un oggetto
ClipData
il cui singolo oggettoClipData.Item
contiene unIntent
. L'etichetta dell'oggettoClipDescription
è impostata sulabel
. Il tipo MIME è impostato suMIMETYPE_TEXT_INTENT
.Usa
newIntent()
per creare un clip da un oggettoIntent
.
Forzare i dati degli appunti in testo
Anche se la tua applicazione gestisce solo il testo, puoi copiare i dati non di testo dalla clipboard convertendoli con il metodoClipData.Item.coerceToText()
.
Questo metodo converte i dati in ClipData.Item
in testo e restituisce un
CharSequence
. Il valore restituito da ClipData.Item.coerceToText()
si basa sulla forma dei dati in ClipData.Item
:
- Testo
-
Se
ClipData.Item
è un testo, ovvero segetText()
non è null, coerceToText() restituisce il testo. - URI
-
Se
ClipData.Item
è un URI, ovvero segetUri()
non è nullo,coerceToText()
tenta di utilizzarlo come URI dei contenuti.- Se l'URI è un URI di contenuti e il provider può restituire uno stream di testo,
coerceToText()
restituisce uno stream di testo. - Se l'URI è un URI dei contenuti, ma il fornitore non offre uno stream di testo,
coerceToText()
restituisce una rappresentazione dell'URI. La rappresentazione è la stessa restituita daUri.toString()
. - Se l'URI non è un URI dei contenuti,
coerceToText()
restituisce una rappresentazione dell'URI. La rappresentazione è la stessa restituita daUri.toString()
.
- Se l'URI è un URI di contenuti e il provider può restituire uno stream di testo,
- Intent
- Se
ClipData.Item
è unIntent
, ovvero segetIntent()
non è nullo,coerceToText()
lo converte in un URI intent e lo restituisce. La rappresentazione è la stessa di quella restituita daIntent.toUri(URI_INTENT_SCHEME)
.
Il framework degli appunti è riassunto nella figura 2. Per copiare i dati, un'applicazione inserisce un oggetto ClipData
negli Appunti globali ClipboardManager
. ClipData
contiene uno o più oggetti ClipData.Item
e un oggetto ClipDescription
. Per incollare i dati, un'applicazione ottiene il ClipData
, il relativo tipo MIME dal ClipDescription
e i dati dal ClipData.Item
o dal provider di contenuti a cui fa riferimento il ClipData.Item
.
Copia negli appunti
Per copiare i dati negli appunti, ottieni un handle nell'oggetto ClipboardManager
globale,
crea un oggetto ClipData
e aggiungi all'oggetto ClipDescription
e uno o più
ClipData.Item
oggetti. Quindi, aggiungi l'oggetto ClipData
completato all'oggetto ClipboardManager
. Questo aspetto è descritto ulteriormente nella procedura seguente:
- Se copi i dati utilizzando un URI dei contenuti, configura un fornitore di contenuti.
- Scarica gli appunti di sistema:
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);
-
Copia i dati in un nuovo oggetto
ClipData
:-
Per il testo
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!");
-
Per un URI
Questo snippet costruisce un URI codificando un ID record nell'URI dei contenuti per il provider. Questa tecnica è descritta in modo più dettagliato nella sezione Codifica di un identificatore nell'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);
-
Per un'intenzione
Questo snippet crea un
Intent
per un'applicazione e poi lo inserisce nell'oggetto 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);
-
Per il testo
-
Inserisci il nuovo oggetto clip negli appunti:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
Fornisci un feedback durante la copia negli appunti
Gli utenti si aspettano un feedback visivo quando un'app copia i contenuti nella clipboard. Questa operazione viene eseguita automaticamente per gli utenti con Android 13 e versioni successive, ma deve essere implementata manualmente nelle versioni precedenti.
A partire da Android 13, il sistema mostra una conferma visiva standard quando vengono aggiunti contenuti agli appunti. La nuova conferma esegue quanto segue:
- Conferma che i contenuti sono stati copiati correttamente.
- Fornisce un'anteprima dei contenuti copiati.
In Android 12L (livello API 32) e versioni precedenti, gli utenti potrebbero non essere sicuri di aver copiato correttamente i contenuti o di cosa hanno copiato. Questa funzionalità standardizza le varie notifiche mostrate dalle app dopo la copia e offre agli utenti un maggiore controllo sulla clipboard.
Evitare notifiche duplicate
In Android 12L (livello API 32) e versioni precedenti, consigliamo di avvisare gli utenti quando eseguono una copia riuscita inviando un feedback visivo in-app, utilizzando un widget come Toast
o Snackbar
, dopo la copia.
Per evitare la visualizzazione duplicata delle informazioni, ti consigliamo vivamente di rimuovere le notifiche popup o le barre di appostamento visualizzate dopo una copia in-app per Android 13 e versioni successive.
Ecco un esempio di come implementare questa funzionalità:
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() }
Aggiungere contenuti sensibili agli appunti
Se la tua app consente agli utenti di copiare contenuti sensibili nella clipboard, ad esempio password o dati della carta di credito, devi aggiungere un flag a ClipDescription
in ClipData
prima di chiamare ClipboardManager.setPrimaryClip()
. L'aggiunta di questo flag impedisce la visualizzazione di contenuti sensibili nella conferma visiva dei contenuti copiati in Android 13 e versioni successive.
Per segnalare contenuti sensibili, aggiungi un extra booleano alla ClipDescription
. Tutte le app devono eseguire questa operazione, indipendentemente dal livello API target.
// 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) } }
Incolla dagli appunti
Come descritto in precedenza, incolla i dati dalla clipboard recuperando l'oggetto clipboard globale, recuperando l'oggetto clip, esaminando i relativi dati e, se possibile, copiandoli dall'oggetto clip nel tuo spazio di archiviazione. Questa sezione spiega nel dettaglio come incollare le tre forme di dati della clipboard.
Incolla testo normale
Per incollare il testo normale, ottieni la clipboard globale e verifica che possa restituire il testo normale. Quindi recupera
l'oggetto clip e copiane il testo nel tuo spazio di archiviazione utilizzando getText()
, come descritto
nella procedura seguente:
- Recupera l'oggetto
ClipboardManager
globale utilizzandogetSystemService(CLIPBOARD_SERVICE)
. Inoltre, dichiara che una variabile globale contenga il testo incollato:Kotlin
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
Java
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
- Determina se devi attivare o disattivare l'opzione "Incolla" nell'attività corrente. Verifica che la clipboard contenga un clip e che tu possa gestire il tipo di dati
rappresentato dal clip:
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); }
- Copia i dati dagli appunti. Questo punto del codice è raggiungibile solo se il
menu "Incolla" è abilitato, quindi puoi presumere che la clipboard contenga text normale. Non sai ancora se contiene una stringa di testo o un URI che rimanda a un testo normale.
Lo snippet di codice riportato di seguito verifica questo aspetto, ma mostra solo il codice per la gestione del testo normale:
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; } }
Incollare i dati da un URI contenuto
Se l'oggetto ClipData.Item
contiene un URI dei contenuti e hai stabilito di poter gestire uno dei relativi tipi MIME, crea un ContentResolver
e chiama il metodo del fornitore di contenuti appropriato per recuperare i dati.
La seguente procedura descrive come ottenere i dati da un fornitore di contenuti in base a un URI di contenuto negli appunti. Verifica se un tipo MIME che l'applicazione può utilizzare è disponibile dal fornitore.
-
Dichiara una variabile globale contenente il tipo 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";
- Scarica gli appunti globali. Ottieni anche un risolutore dei contenuti per accedere al fornitore di contenuti:
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();
- Scarica il clip principale dagli appunti e recupera i contenuti come 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();
- Verifica se l'URI è un URI contenuto chiamando
getType(Uri)
. Questo metodo restituisce null seUri
non punta a un fornitore di contenuti valido.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);
- Verifica se il fornitore di contenuti supporta un tipo MIME riconosciuto dall'applicazione. Se
è così, chiama
ContentResolver.query()
per recuperare i dati. Il valore restituito è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(); } } } }
Incolla un intent
Per incollare un intent, devi prima recuperare gli appunti globali. Esamina l'oggetto ClipData.Item
per vedere se contiene un Intent
. Quindi chiama getIntent()
per copiare
l'intent nel tuo spazio di archiviazione. Il seguente snippet lo dimostra:
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. }
Notifica di sistema visualizzata quando la tua app accede ai dati degli appunti
Su Android 12 (livello API 31) e versioni successive, in genere il sistema mostra un messaggio popup quando la tua app chiama getPrimaryClip()
.
Il testo all'interno del messaggio ha il seguente formato:
APP pasted from your clipboard
Il sistema non mostra un messaggio popup quando la tua app esegue una delle seguenti operazioni:
- Accedi
ClipData
dalla tua app. - Accede ripetutamente a
ClipData
da un'app specifica. La notifica viene visualizzata solo quando la tua app accede ai dati di quell'app per la prima volta. - Recupera i metadati per l'oggetto clip, ad esempio chiamando
getPrimaryClipDescription()
anzichégetPrimaryClip()
.
Utilizzare i fornitori di contenuti per copiare dati complessi
I fornitori di contenuti supportano la copia di dati complessi come record di database o stream di file. Per copiare i dati, inserisci un URI dei contenuti negli appunti. Le applicazioni di incollaggio recuperano questo URI dalla clipboard e lo utilizzano per recuperare i dati del database o i descrittori dello stream di file.
Poiché l'applicazione di incollaggio ha solo l'URI dei contenuti per i tuoi dati, deve sapere quale elemento recuperare. Puoi fornire queste informazioni codificando un identificatore per i dati nell'URI stesso oppure puoi fornire un URI univoco che restituisca i dati che vuoi copiare. La tecnica che scegli dipende dall'organizzazione dei dati.
Le sezioni seguenti descrivono come configurare gli URI, fornire dati complessi e fornire stream di file. Le descrizioni presuppongono che tu abbia familiarità con i principi generali del design per i fornitori di contenuti.
Codifica un identificatore nell'URI
Una tecnica utile per copiare i dati negli appunti con un URI è codificare un identificatore per i dati nell'URI stesso. Il fornitore di contenuti può quindi recuperare l'identificatore dall'URI e utilizzarlo per recuperare i dati. L'applicazione di incollaggio non deve sapere che l'identificatore esiste. Deve solo recuperare il "riferimento", ovvero l'URI più l'identificatore, dalla clipboard, fornirlo al fornitore di contenuti e recuperare i dati.
In genere codifichi un identificatore su un URI di contenuti concatenandolo alla fine dell'URI. Ad esempio, supponi di definire l'URI del fornitore come la seguente stringa:
"content://com.example.contacts"
Se vuoi codificare un nome in questo URI, utilizza il seguente snippet di codice:
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);
Se utilizzi già un fornitore di contenuti, ti consigliamo di aggiungere un nuovo percorso URI che indichi che l'URI è destinato alla copia. Ad esempio, supponiamo che tu abbia già i seguenti percorsi URI:
"content://com.example.contacts/people" "content://com.example.contacts/people/detail" "content://com.example.contacts/people/images"
Puoi aggiungere un altro percorso per la copia degli URI:
"content://com.example.contacts/copying"
Puoi quindi rilevare un URI "copia" tramite la corrispondenza di pattern e gestirlo con codice specifico per la copia e l'incollaggio.
In genere, la tecnica di codifica viene utilizzata se utilizzi già un fornitore di contenuti, un database interno o una tabella interna per organizzare i dati. In questi casi, hai più dati da copiare e presumibilmente un identificatore univoco per ogni dato. In risposta a una query dell'applicazione di inserimento, puoi cercare i dati in base al relativo identificatore e restituirli.
Se non hai più elementi di dati, probabilmente non è necessario codificare un identificatore. Puoi utilizzare un URI univoco per il tuo provider. In risposta a una query, il fornitore restituisce i dati che contiene al momento.
Copia strutture di dati
Configura un fornitore di contenuti per copiare e incollare dati complessi come sottoclasse del componente ContentProvider
. Codifica l'URI inserito negli appunti in modo che indichi il record esatto che vuoi fornire. Inoltre, tieni presente lo stato esistente della tua applicazione:
- Se hai già un fornitore di contenuti, puoi aumentare le sue funzionalità. Potrebbe essere necessario solo
modificare il metodo
query()
per gestire gli URI provenienti da applicazioni che vogliono incollare i dati. Probabilmente vuoi modificare il metodo per gestire un pattern URI "copy". - Se la tua applicazione gestisce un database interno, ti consigliamo di spostarlo in un fornitore di contenuti per facilitarne la copia.
- Se non utilizzi un database, puoi implementare un semplice fornitore di contenuti il cui unico scopo è offrire dati alle applicazioni che incollano dagli appunti.
Nel provider di contenuti, sostituisci almeno i seguenti metodi:
-
query()
- Le applicazioni di incollaggio presuppongono di poter ottenere i tuoi dati utilizzando questo metodo con l'URI che hai inserito nella clipboard. Per supportare la copia, chiedi a questo metodo di rilevare gli URI contenenti un percorso "copia" speciale. L'applicazione può quindi creare un URI "copy" da inserire negli appunti, contenente il percorso di copia e un puntatore al record esatto da copiare.
-
getType()
- Questo metodo deve restituire i tipi MIME per i dati che intendi copiare. Il metodo
newUri()
chiamagetType()
per inserire i tipi MIME nel nuovo oggettoClipData
.I tipi MIME per i dati complessi sono descritti in Fornitori di contenuti.
Non è necessario disporre di altri metodi per fornitori di contenuti, come
insert()
o
update()
.
Un'applicazione di incollaggio deve solo recuperare i tipi MIME supportati e copiare i dati dal provider.
Se hai già questi metodi, non interferiranno con le operazioni di copia.
I seguenti snippet mostrano come configurare l'applicazione per copiare dati complessi:
-
Nelle costanti globali per la tua applicazione, dichiara una stringa URI di base e un percorso che identifica le stringhe URI che utilizzi per copiare i dati. Dichiara anche un tipo MIME per i dati copiati.
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";
- Nell'attività da cui gli utenti copiano i dati, configura il codice per copiare i dati negli Appunti.
In risposta a una richiesta di copia, inserisci l'URI negli appunti.
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);
-
Nell'ambito globale del tuo fornitore di contenuti, crea un'associazione URI e aggiungi un pattern URI che corrisponda agli URI che inserisci negli appunti.
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);
-
Configura il metodo
query()
. Questo metodo è in grado di gestire diversi pattern URI, a seconda di come lo codifichi, ma viene visualizzato solo il pattern per l'operazione di copia degli appunti.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. ... }
-
Configura il metodo
getType()
in modo che restituisca un tipo MIME appropriato per i dati copiati: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 sezione Incollare i dati da un URI contenuto descrive come ottenere un URI contenuto dalla clipboard e utilizzarlo per recuperare e incollare i dati.
Copiare gli stream di dati
Puoi copiare e incollare grandi quantità di dati di testo e binari sotto forma di stream. I dati possono avere forme come le seguenti:
- I file archiviati sul dispositivo effettivo
- Stream da socket
- Grandi quantità di dati archiviati nel sistema di database sottostante di un provider
Un fornitore di contenuti per gli stream di dati fornisce l'accesso ai propri dati con un oggetto descrittore file, come AssetFileDescriptor
, anziché con un oggetto Cursor
. L'applicazione di incollaggio legge lo stream di dati utilizzando questo descriptor file.
Per configurare l'applicazione in modo che copi uno stream di dati con un fornitore:
-
Configura un URI dei contenuti per lo stream di dati che stai inserendo nella clipboard. Ecco alcune opzioni a tua disposizione:
- Codifica un identificatore per lo stream di dati nell'URI, come descritto nella sezione Codificare un identificatore sull'URI, quindi gestisci una tabella nel provider che contenga gli identificatori e il nome dello stream corrispondente.
- Codifica il nome dello stream direttamente nell'URI.
- Utilizza un URI univoco che restituisca sempre il flusso attuale del provider. Se utilizzi questa opzione, ricordati di aggiornare il provider in modo che indichi un altro stream ogni volta che copi lo stream negli appunti utilizzando l'URI.
- Fornisci un tipo MIME per ogni tipo di stream di dati che prevedi di offrire. Le applicazioni di incollaggio hanno bisogno di queste informazioni per determinare se possono incollare i dati negli Appunti.
- Implementa uno dei metodi
ContentProvider
che restituisce un descrittore di file per un flusso. Se codifichi gli identificatori nell'URI dei contenuti, utilizza questo metodo per determinare quale stream aprire. - Per copiare lo stream di dati negli appunti, crea l'URI dei contenuti e inseriscilo negli appunti.
Per incollare uno stream di dati, un'applicazione recupera il clip dalla clipboard, recupera l'URI e lo utilizza in una chiamata a un metodo descrittore file ContentResolver
che apre lo stream. Il metodo ContentResolver
chiama il metodo ContentProvider
corrispondente, trasmettendogli l'URI del contenuto. Il provider restituisce il descrittore file al metodo ContentResolver
. L'applicazione di incollaggio ha quindi la responsabilità di leggere i dati dallo stream.
Il seguente elenco mostra i metodi più importanti del descrittore di file per un fornitore di contenuti. Ognuno di questi ha un metodo ContentResolver
corrispondente con la stringa "Descriptor" aggiunta al nome del metodo. Ad esempio, l'analogo di ContentResolver
è openAssetFile()
.openAssetFileDescriptor()
-
openTypedAssetFile()
-
Questo metodo restituisce un descrittore file della risorsa, ma solo se il tipo MIME fornito è supportato dal fornitore. L'autore della chiamata, ovvero l'applicazione che esegue l'incollaggio, fornisce un pattern di tipo MIME. Il provider di contenuti dell'applicazione che copia un URI nella clipboard restituisce un handle file
AssetFileDescriptor
se può fornire il tipo MIME e genera un'eccezione se non può.Questo metodo gestisce le sottosezioni dei file. Puoi utilizzarla per leggere gli asset che il fornitore di contenuti ha copiato negli appunti.
-
openAssetFile()
-
Questo metodo è una forma più generale di
openTypedAssetFile()
. Non filtra i tipi MIME consentiti, ma può leggere le sottosezioni dei file. -
openFile()
-
Si tratta di una forma più generica di
openAssetFile()
. Non può leggere le sottosezioni dei file.
Se vuoi, puoi utilizzare il metodo
openPipeHelper()
con il metodo del descrittore file. In questo modo, l'applicazione di incollaggio può leggere i dati dello stream in un THREAD in background utilizzando un pipe. Per utilizzare questo metodo, implementa l'interfaccia ContentProvider.PipeDataWriter
.
Progetta funzionalità di copia e incolla efficaci
Per progettare una funzionalità di copia e incolla efficace per la tua applicazione, ricordati questi punti:
- C'è un solo clip negli appunti in qualsiasi momento. Una nuova operazione di copia da parte di qualsiasi applicazione del sistema sovrascrive il clip precedente. Poiché l'utente potrebbe uscire dall'applicazione e copiarlo prima di tornare, non puoi presumere che gli appunti contengano il clip che l'utente ha precedentemente copiato nella tua applicazione.
-
Lo scopo previsto di più oggetti
ClipData.Item
per clip è supportare la copia e l'incollaggio di più selezioni anziché diverse forme di riferimento a una singola selezione. In genere, è preferibile che tutti gli oggettiClipData.Item
di un clip abbiano la stessa forma. In altre parole, devono essere tutti di tipo testo semplice, URI dei contenuti oIntent
e non misti. -
Quando fornisci i dati, puoi offrire diverse rappresentazioni MIME. Aggiungi i tipi MIME supportati al
ClipDescription
, quindi implementali nel tuo fornitore di contenuti. -
Quando ricevi dati dalla clipboard, è responsabilità della tua applicazione controllare i tipi MIME disponibili e decidere quale, se presente, utilizzare. Anche se è presente un clip nella clipboard e l'utente richiede l'applicazione di un'operazione di incolla, la tua applicazione non è tenuta a eseguire l'operazione. Esegui l'operazione di incolla se il tipo MIME è compatibile. Puoi forzare i dati
negli Appunti in testo utilizzando
coerceToText()
. Se la tua applicazione supporta più di uno dei tipi MIME disponibili, puoi lasciare che sia l'utente a scegliere quale utilizzare.