O Android fornece um poderoso framework baseado em área de transferência para os procedimentos de copiar e colar. Ele é compatível com tipos de dados simples e complexos, incluindo strings de texto, estruturas de dados complexas, dados de texto e de fluxo binário, e até mesmo recursos de aplicativo. Dados de texto simples são armazenados diretamente na área de transferência, ao passo que dados complexos são armazenados como uma referência que o aplicativo de colagem resolve com um provedor de conteúdo. Copiar e colar funciona tanto dentro do aplicativo quanto entre aplicativos que implementam o framework.
Como parte do framework usa provedores de conteúdo, este tópico pressupõe certa familiaridade com a API Android Content Provider, que é descrita no tópico Provedores de conteúdo.
Os usuários esperam feedback ao copiar conteúdo para a área de transferência. Portanto, além do framework que permite copiar e colar, o sistema mostra uma IU padrão quando usuários executam ações de cópia no Android 13 (API de nível 33) e versões mais recentes. Continue enviando feedback manualmente aos usuários que executam ações de cópia no Android 12L (API de nível 32) e versões anteriores. Veja nossas recomendações sobre como fazer isso posteriormente neste guia.
O framework da área de transferência
Ao usar o framework da área de transferência, você coloca os dados em um objeto de transferência e depois coloca esse objeto na área de transferência do sistema. O objeto de transferência pode assumir uma destas três formas:
- Texto
- Uma string de texto. Você coloca a string diretamente no objeto de transferência e depois põe esse objeto na área de transferência. Para colar a string, pegue o objeto de transferência na área de transferência e copie a string no armazenamento do seu aplicativo.
- URI
-
Um objeto
Uri
que representa qualquer forma de URI. Isso é feito principalmente para copiar dados complexos de um provedor de conteúdo. Para copiar dados, você coloca um objetoUri
em um objeto de transferência e põe esse objeto na área de transferência. Para colar os dados, pegue o objeto de transferência e o objetoUri
, resolva-o em uma fonte de dados, como um provedor de conteúdo, e copie os dados da origem para o armazenamento do seu aplicativo. - Intent
-
uma
Intent
. Ela oferece suporte à cópia de atalhos de aplicativos. Para copiar dados, crie uma intent, coloque-a em um objeto de transferência e ponha esse objeto na área de transferência. Para colar os dados, pegue o objeto de transferência e copie o objeto da intent na área de memória do seu aplicativo.
A área de transferência mantém apenas um objeto de transferência por vez. Quando um aplicativo coloca um objeto de transferência na área de transferência, o objeto de transferência anterior desaparece.
Não é necessário gerenciar todos os tipos de dados para permitir que os usuários colem dados no seu aplicativo. Você pode examinar os dados na área de transferência antes de oferecer aos usuários a opção de colar. Além de ter um certo formulário de dados, o objeto de transferência também contém metadados que informam os tipos MIME disponíveis. Esses metadados ajudam a decidir se o aplicativo pode fazer algo útil com os dados da área de transferência. Por exemplo, caso você tenha um aplicativo que lida principalmente com texto, você pode ignorar objetos de transferência que contenham um URI ou uma intent.
Também é possível permitir que os usuários colem textos, independentemente da forma de dados na área de transferência. Para fazer isso, é possível fazer com que os dados da área de transferência se tornem uma representação de texto e, em seguida, colar esse texto. Isso é descrito na seção Forçar os dados da área de transferência a se tornarem texto.
Classes da área de transferência
Esta seção descreve as classes usadas pelo framework da área de transferência.
ClipboardManager
No sistema Android, a área de transferência do sistema é representada pela classe
global ClipboardManager
. Essa classe não é iniciada
diretamente. Em vez disso, ela é referenciada chamando
getSystemService(CLIPBOARD_SERVICE)
.
ClipData, ClipData.Item e ClipDescription
Para adicionar dados à área de transferência, crie um objeto ClipData
que
contenha uma descrição dos dados e os dados em si. A área de transferência mantém apenas um
ClipData
por vez. Um ClipData
contém um
objeto ClipDescription
e um ou mais
objetos ClipData.Item
.
Um objeto ClipDescription
contém metadados sobre a transferência. Ele
contém particularmente uma matriz de tipos MIME disponíveis para os dados da transferência. Além disso, no
Android 12 (API de nível 31) e versões mais recentes, os metadados incluem informações sobre
se o objeto contém
texto estilizado e sobre
o tipo de
texto no objeto. Quando você coloca dados na área de transferência, essa informação é disponibilizada para
aplicativos de colagem, que podem examiná-la para ver se ela pode processar os dados.
Um objeto ClipData.Item
contém dados de texto, URI ou intent:
- Texto
-
Um
CharSequence
. - URI
-
Um
Uri
. Ele geralmente inclui um URI do provedor de conteúdo, embora qualquer URI seja permitido. O aplicativo que fornece os dados coloca o URI na área de transferência. Os aplicativos que querem colar os dados recebem o URI da área de transferência e o usam para acessar o provedor de conteúdo (ou outra fonte de dados) e recuperar os dados. - Intent
-
Uma
Intent
. Esse tipo de dados permite que você copie um atalho de aplicativo para a área de transferência. Então, os usuários podem colar o atalho nos aplicativos deles para uso posterior.
Você pode adicionar mais de um objeto ClipData.Item
a uma transferência. Isso permite que
os usuários copiem e colem várias seleções como uma única transferência. Por exemplo, se você tiver um widget
de lista que permite que o usuário selecione mais de um item por vez, é possível copiar todos os itens
para a área de transferência de uma vez. Para fazer isso, crie um ClipData.Item
separado
para cada item da lista e, em seguida, adicione os
objetos ClipData.Item
ao objeto ClipData
.
Métodos de conveniência ClipData
A classe ClipData
fornece métodos estáticos de conveniência para criar
um objeto ClipData
usando um objeto ClipData.Item
e um objeto ClipDescription
simples:
-
newPlainText(label, text)
-
Retorna um objeto
ClipData
cujo único objetoClipData.Item
contém uma string de texto. O rótulo do objetoClipDescription
está definido comolabel
. O tipo MIME único emClipDescription
éMIMETYPE_TEXT_PLAIN
.Use
newPlainText()
para criar uma transferência a partir de uma string de texto. -
newUri(resolver, label, URI)
-
Retorna um objeto
ClipData
cujo único objetoClipData.Item
contém um URI. O rótulo do objetoClipDescription
está definido comolabel
. Se o URI for de conteúdo (Uri.getScheme()
retornacontent:
), o método vai usar o objetoContentResolver
fornecido emresolver
para extrair os tipos MIME disponíveis no provedor de conteúdo e armazená-los naClipDescription
. Para URIs que não sejam decontent:
, o método definirá o tipo MIME comoMIMETYPE_TEXT_URILIST
.Use
newUri()
para criar uma transferência a partir de um URI, principalmente de um URIcontent:
. -
newIntent(label, intent)
-
Retorna um objeto
ClipData
cujo único objetoClipData.Item
contém umaIntent
. O rótulo do objetoClipDescription
está definido comolabel
. O tipo MIME está definido comoMIMETYPE_TEXT_INTENT
.Use
newIntent()
para criar uma transferência a partir de um objeto de intent.
Forçar os dados da área de transferência a se tornarem texto
Mesmo que seu aplicativo só processe texto, é possível copiar dados não textuais da
área de transferência convertendo esses dados com o método
ClipData.Item.coerceToText()
.
Esse método converte os dados de ClipData.Item
em texto e
retorna uma CharSequence
. O valor retornado por
ClipData.Item.coerceToText()
é baseado na forma dos dados em ClipData.Item
:
- Texto
-
Se
ClipData.Item
for um texto (getText()
não for nulo),coerceToText()
retorna o texto. - URI
-
Se
ClipData.Item
for um URI (getUri()
não for nulo),coerceToText()
tentará usá-lo como URI de conteúdo:-
Se o URI for de conteúdo e o provedor puder retornar um fluxo de texto,
coerceToText()
retornará um fluxo de texto. -
Se o URI for um URI de conteúdo, mas o provedor não oferecer um fluxo de texto,
coerceToText()
retornará uma representação do URI. A representação é a mesma retornada porUri.toString()
. -
Se o URI não for de conteúdo,
coerceToText()
retornará uma representação do URI. A representação é a mesma retornada porUri.toString()
.
-
Se o URI for de conteúdo e o provedor puder retornar um fluxo de texto,
- Intent
-
Se
ClipData.Item
for uma intent (getIntent()
não for nulo), o métodocoerceToText()
vai convertê-la em um URI de intent e a retornar. A representação é a mesma retornada porIntent.toUri(URI_INTENT_SCHEME)
.
O framework de área de transferência está resumido na figura 1. Para copiar dados, um aplicativo coloca um
objeto ClipData
na área de transferência
global ClipboardManager
. O ClipData
contém um ou mais
objetos ClipData.Item
e um
ClipDescription
. Para colar dados, um aplicativo recebe o
ClipData
, consegue o tipo MIME dele em
ClipDescription
e recebe os dados do
ClipData.Item
ou do provedor de conteúdo referido por
ClipData.Item
.

Figura 1. O framework da área de transferência do Android.
Copiar para a área de transferência
Como descrito anteriormente, para copiar dados para a área de transferência, use um handle para o objeto
ClipboardManager
global, crie um objeto ClipData
,
adicione um ClipDescription
e um ou mais
objetos ClipData.Item
a ele e depois adicione o objeto
ClipData
finalizado ao objeto ClipboardManager
.
Isso é descrito em detalhes no seguinte procedimento:
-
Se estiver copiando dados usando um URI de conteúdo, configure
um provedor de conteúdo.
O aplicativo de amostra Note Pad é um exemplo de como usar um provedor de conteúdo para copiar e colar. A classe NotePadProvider implementa o provedor de conteúdo. A classe NotePad define um contrato entre o provedor e outros aplicativos, incluindo os tipos MIME compatíveis.
-
Receba a área de transferência do 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);
-
Copie os dados para um novo objeto
ClipData
:-
Para texto
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!");
-
Para um URI
Este snippet cria um URI codificando um código de registro no URI de conteúdo do provedor. Essa técnica é abordada com mais detalhes na seção Codificar um identificador no URI:
Kotlin
// Creates a Uri based on 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 that you use 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 based on 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 that you use 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);
-
Para uma intent
Este snippet constrói uma intent para um aplicativo e a coloca no objeto de transferência:
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);
-
-
Coloque o novo objeto de transferência na área de transferência:
Kotlin
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip)
Java
// Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
Enviar feedback durante a realização de cópias para a área de transferência
Os usuários esperam feedback visual quando usam um app para copiar conteúdo para a área de transferência. Isso é feito automaticamente para usuários no Android 13 e versões mais recentes, mas precisa ser implementado manualmente em versões anteriores.
A partir do Android 13, o sistema mostra uma confirmação visual padrão ao adicionar conteúdo à área de transferência. A nova confirmação faz o seguinte:
- Confirma que o conteúdo foi copiado.
- Oferece uma visualização do conteúdo copiado.

Evitar notificações duplicadas
No Android 12L (API de nível 32) e versões anteriores, sugerimos alertar os usuários sobre a conclusão da cópia com a emissão de um widget pop-up visual no app (como toasts ou snackbars) após a cópia.
Para não mostrar informações duplicadas, recomendamos que você remova os widgets pop-up exibidos quando o usuário copia um conteúdo no app no Android 13 e versões mais recente.


Veja como fazer isso:
fun textCopyThenPost(textCopied:String) { val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager // When setting the clip board 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() }
Adicionar conteúdo sensível à área de transferência
Se o app permitir que os usuários copiem conteúdo sensível para a área de transferência, como senhas ou dados
de cartões de crédito, adicione uma sinalização à ClipDescription
do
ClipData antes de chamar ClipboardManager#setPrimaryClip()
. No Android 13 e versões
mais recentes, essa sinalização impede que conteúdo sensível seja exibido em qualquer visualização de conteúdo na confirmação visual de cópia.


Para sinalizar a presença de conteúdo sensível, adicione um booleano extra à ClipDescription
. Isso
precisa ser feito por todos os apps, independentemente do nível desejado da API.
// When your app targets API level 33 or higher clipData.apply { description.extras = PersistableBundle().apply { putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true) } } // If your app targets a lower API level clipData.apply { description.extras = PersistableBundle().apply { putBoolean("android.content.extra.IS_SENSITIVE", true) } }
Colar da área de transferência
Como descrito anteriormente, para colar os dados da área de transferência, pegue o objeto global da área de transferência, pegue o objeto de transferência, observe seus dados e, se possível, copie os dados do objeto de transferência para seu próprio armazenamento. Esta seção explica em detalhes como colar os três formatos de dados da área de transferência.
Colar texto simples
Para colar um texto simples, primeiro pegue a área de transferência global e verifique se ela pode retornar texto simples.
Em seguida, pegue o objeto de transferência e copie o texto dele para seu armazenamento usando
getText()
, conforme descrito no procedimento a seguir:
-
Consiga o objeto
ClipboardManager
global usandogetSystemService(CLIPBOARD_SERVICE)
. Declare também uma variável global para conter o texto colado:Kotlin
var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager var pasteData: String = ""
Java
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); String pasteData = "";
-
Em seguida, determine se você ativará ou desativará a opção "colar" na
atividade atual. Verifique se a área de transferência contém uma transferência e se
é possível processar os tipos de dados representados pela transferência:
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 if you can handle the data. pasteItem.isEnabled = when { !clipboard.hasPrimaryClip() -> { false } !(clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) -> { // This disables the paste menu item, since the clipboard has data but it is not plain text false } else -> { // This 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 if you can handle the data. if (!(clipboard.hasPrimaryClip())) { pasteItem.setEnabled(false); } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) { // This disables the paste menu item, since the clipboard has data but it is not plain text pasteItem.setEnabled(false); } else { // This enables the paste menu item, since the clipboard contains plain text. pasteItem.setEnabled(true); }
-
Copie os dados da área de transferência. Este ponto do programa só pode ser acessado se o
item de menu "colar" estiver ativado, então você pode presumir que a área de transferência contém
texto simples. Você ainda não sabe se ela contém uma string de texto ou um URI que aponta para texto
simples. O snippet a seguir testa isso, mas ele só mostra o código para processar texto simples:
Kotlin
when (menuItem.itemId) { ... R.id.menu_paste -> { // Responds to the user selecting "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. 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 does not 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 is not // presented here. pasteData = resolveUri(pasteUri) true } else { // Something is wrong. The MIME type was plain text, but the clipboard does not // contain either 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 does not 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 is not // presented here. pasteData = resolveUri(Uri); return true; } else { // Something is wrong. The MIME type was plain text, but the clipboard does not contain either // text or a Uri. Report an error. Log.e(TAG, "Clipboard contains an invalid data type"); return false; } }
Colar dados de um URI de conteúdo
Se o objeto ClipData.Item
contém um URI de conteúdo e você
determinou que pode processar um dos tipos MIME dele, crie um
ContentResolver
e chame o método adequado do provedor de conteúdo
para recuperar os dados.
O procedimento a seguir descreve como conseguir dados de um provedor de conteúdo com base em um URI de conteúdo na área de transferência. Ele verifica se o tipo MIME que o aplicativo pode usar está disponível no provedor:
-
Declare uma variável global para conter o 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";
-
Pegue a área de transferência global. Receba também um resolvedor de conteúdo para acessar o provedor
de conteúdo:
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();
-
Receba a principal transferência da área de transferência e receba o conteúdo dela como um 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();
-
Teste se o URI é de conteúdo chamando
getType(Uri)
. Esse método retornará nulo seUri
não apontar para um provedor de conteúdo válido: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);
-
Teste se o provedor de conteúdo é compatível com um tipo MIME que o aplicativo atual
entenda. Caso seja, chame
ContentResolver.query()
para receber os dados. O valor de retorno é umCursor
:Kotlin
// If the return value is not 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 will vary according to the // format of the data model. } // Kotlin `use` will automatically close the Cursor } } } }
Java
// If the return value is not 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 will vary according to the // format of the data model. } } // close the Cursor pasteCursor.close(); } } } }
Colar uma intent
Para colar uma intent, primeiro acesse a área de transferência global. Examine o
objeto ClipData.Item
para ver se ele tem uma intent. Então, chame
getIntent()
para copiar a intent no seu armazenamento.
O snippet a seguir demonstra isso:
Kotlin
// Gets a handle to the Clipboard Manager val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager // Checks to see if the clip item contains an Intent, by testing to see if 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 your application was expecting an Intent to be // on the clipboard }
Java
// Gets a handle to the Clipboard Manager ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); // Checks to see if the clip item contains an Intent, by testing to see if 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 your application was expecting an Intent to be // on the clipboard }
Notificação do sistema exibida quando o app acessa dados da área de transferência
No Android 12 (API de nível 31) e versões mais recentes, o sistema geralmente mostra uma mensagem
de aviso quando o app chama
getPrimaryClip()
.
O texto da mensagem contém o formato a seguir:
APP pasted from your clipboard
O sistema não mostrará uma mensagem de aviso quando seu app fizer uma das seguintes ações:
- Acessar
ClipData
no seu próprio app. - Acessar
ClipData
repetidamente de um app específico. A notificação de aviso aparecerá somente quando seu app acessar os dados desse app pela primeira vez. - Recuperar metadados para o objeto de transferência, por exemplo, chamando
getPrimaryClipDescription()
, em vez degetPrimaryClip()
.
Usar provedores de conteúdo para copiar dados complexos
Os provedores de conteúdo são compatíveis com a cópia de dados complexos, como registros de banco de dados ou fluxos de arquivos. Para copiar os dados, coloque um URI de conteúdo na área de transferência. Em seguida, os aplicativos de colagem pegam esse URI da área de transferência e usam para recuperar dados do banco de dados ou descritores do fluxo de arquivos.
Como o aplicativo de colagem só tem o URI de conteúdo para seus dados, ele precisa saber quais dados recuperar. É possível pode fornecer essas informações codificando um identificador para os dados no próprio URI ou fornecendo um URI exclusivo que retorne os dados que você quer copiar. A técnica escolhida depende da organização dos seus dados.
As seções a seguir descrevem como configurar URIs, fornecer dados complexos e fornecer fluxos de arquivos. As descrições pressupõem que você esteja familiarizado com os princípios gerais do design de provedores de conteúdo.
Codificar um identificador no URI
Uma técnica útil para copiar dados para a área de transferência com um URI é codificar um identificador para os dados no próprio URI. Seu provedor de conteúdo pode, então, conseguir o identificador do URI e usá-lo para recuperar os dados. O aplicativo de colagem não precisa saber que o identificador existe. Ele só precisa conseguir a "referência" (o URI mais o identificador) da área de transferência, fornecê-la para seu provedor de conteúdo e recuperar os dados.
Normalmente, um identificador é codificado em um URI de conteúdo ao concatená-lo no fim do URI. Por exemplo, suponha que você definiu seu URI de provedor como a string a seguir:
"content://com.example.contacts"
Caso queira codificar um nome para este URI, use o snippet abaixo:
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 você já está usando um provedor de conteúdo, pode adicionar um novo caminho de URI que indique que o URI é voltado para a ação de copiar. Por exemplo, suponha que você já tenha os seguintes caminhos de URI:
"content://com.example.contacts"/people "content://com.example.contacts"/people/detail "content://com.example.contacts"/people/images
Você pode adicionar outro caminho que seja específico para URIs de cópia:
"content://com.example.contacts/copying"
Em seguida, você pode detectar um URI de cópia por correspondência de padrões e processá-lo com um código específico para copiar e colar.
Normalmente, a técnica de codificação é usada se você já estiver usando um provedor de conteúdo, um banco de dados interno ou uma tabela interna para organizar seus dados. Nesses casos, há várias partes de dados a serem copiados e, possivelmente, um identificador exclusivo para cada uma. Em resposta a uma consulta do aplicativo de colagem, você pode pesquisar os dados pelo identificador e retorná-los.
Se você não tiver vários dados, provavelmente não precisará codificar um identificador. Você poderá simplesmente usar um URI exclusivo para seu provedor. Em resposta a uma consulta, seu provedor retornaria os dados que ele contém atualmente.
A obtenção de um único registro por ID é usada no
aplicativo de amostra Note Pad para
abrir uma nota da lista. A amostra usa o campo _id
de um banco de dados
SQL, mas você pode usar qualquer identificador numérico ou de caracteres que quiser.
Copiar estruturas de dados
Configure um provedor de conteúdo para copiar e colar dados complexos como uma subclasse do
componente ContentProvider
. Codifique também o URI colocado
na área de transferência para que ele aponte para o registro exato que você quer fornecer. Além disso, é
necessário considerar o estado existente do seu aplicativo:
-
Se você já tiver um provedor de conteúdo, poderá melhorar a funcionalidade dele. Talvez seja necessário
modificar o método
query()
para lidar com URIs provenientes de aplicativos que querem colar dados. Você provavelmente modificará o método para processar um padrão de URI de cópia. - Caso seu aplicativo mantenha um banco de dados interno, é recomendável mover esse banco de dados para um provedor de conteúdo para facilitar a ação de copiar dele.
- Se você não estiver usando um banco de dados, é possível implementar um provedor de conteúdo simples, cuja única finalidade é oferecer dados a aplicativos que estejam realizando ações de colar da área de transferência.
No provedor de conteúdo, substitua pelo menos os seguintes métodos:
-
query()
- Os aplicativos de colagem presumirão que podem conseguir seus dados usando esse método com o URI que você colocou na área de transferência. Para ter compatibilidade com a ação de copiar, esse método precisa detectar URIs que contenham um caminho de copiar especial. Seu aplicativo pode criar um URI de cópia para colocar na área de transferência, contendo o caminho da ação e um ponteiro para o registro exato que você quer copiar.
-
getType()
-
Esse método precisa retornar os tipos MIME dos dados que você pretende copiar. O método
newUri()
chamagetType()
para colocar os tipos MIME no novo objetoClipData
.Os tipos MIME para dados complexos são descritos no tópico Provedores de conteúdo.
Você não precisa ter nenhum dos outros métodos de provedor de conteúdo, como
insert()
ou
update()
.
Um aplicativo de colagem só precisa conseguir seus tipos MIME compatíveis e copiar dados do
seu provedor. Se você já tem esses métodos, eles não interferem nas operações de copiar.
Os snippets a seguir demonstram como configurar seu aplicativo para copiar dados complexos:
-
Nas constantes globais do seu aplicativo, declare uma string de URI base e um caminho que identifique as strings de URI que você está usando para copiar dados. Declare também um tipo MIME para os dados copiados:
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";
-
Na atividade da qual os usuários copiam dados,
configure o código para copiar dados para a área de transferência. Coloque o URI na área de transferência, em resposta a
uma solicitação de cópia:
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) // Set 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); // Set the clipboard's primary clip. clipboard.setPrimaryClip(clip);
-
No escopo global de seu provedor de conteúdo, crie um correspondente de URI e adicione um padrão de URI que corresponderá aos URIs colocados na área de transferência:
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);
-
Configure o método
query()
. Esse método pode manipular padrões de URI diferentes, dependendo de como forem codificados, mas somente o padrão para a operação de copiar da área de transferência é mostrado: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 -> { // query and return the contact for the requested name. Here you would decode // the incoming URI, query the data model based on the last name, and return 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: // query and return the contact for the requested name. Here you would decode // the incoming URI, query the data model based on the last name, and return the result // as a Cursor. ... }
-
Configure o método
getType()
para retornar um tipo MIME adequado para dados copiados: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); ... } }
A seção Colar dados de um URI de conteúdo descreve como conseguir um URI de conteúdo da área de transferência e usá-lo para receber e colar dados.
Copiar fluxos de dados
Você pode copiar e colar grandes quantidades de texto e dados binários como fluxos. Os dados podem ter formulários como os seguintes:
- Arquivos armazenados no próprio dispositivo.
- Fluxos de soquetes.
- Grandes quantidades de dados armazenados no sistema de banco de dados de um provedor.
Um provedor de conteúdo para fluxos de dados fornece acesso aos dados com um objeto
descritor de arquivo, como AssetFileDescriptor
, em vez de
um objeto Cursor
. O aplicativo de colagem lê o fluxo de dados usando
esse descritor de arquivo.
Para configurar seu aplicativo para copiar um fluxo de dados com um provedor, siga estas etapas:
-
Configure um URI de conteúdo para o fluxo de dados que será colocado na área de transferência. Veja
algumas opções de como fazer isso a seguir:
- Codifique um identificador do fluxo de dados no URI, conforme descrito na seção Codificar um identificador no URI e mantenha uma tabela no seu provedor que contenha identificadores e o nome do fluxo correspondente.
- Codifique o nome do fluxo diretamente no URI.
- Use um URI exclusivo que sempre retorne o fluxo atual do provedor. Se usar essa opção, vai ser necessário lembrar de atualizar seu provedor para apontá-lo a um fluxo diferente sempre que copiar o fluxo para a área de transferência pelo URI.
- Forneça um tipo MIME para cada tipo de fluxo de dados que você pretende oferecer. Os aplicativos de colagem precisam dessas informações para determinar se podem colar os dados na área de transferência.
-
Implemente um dos métodos
ContentProvider
que retorna um descritor de arquivo para um fluxo. Se você codificar identificadores no URI de conteúdo, use esse método para determinar qual fluxo abrir. - Para copiar o fluxo de dados para a área de transferência, construa o URI de conteúdo e coloque-o na área de transferência.
Para colar um fluxo de dados, um aplicativo toma os dados da área de transferência, consegue o URI e
usa em uma chamada para um método de descritor de arquivo ContentResolver
que
abre o fluxo. O método ContentResolver
chama o método
ContentProvider
correspondente, passando o URI de conteúdo. Seu provedor
retorna o descritor de arquivo para o método ContentResolver
. O aplicativo
de colagem fica, então, responsável por ler os dados do fluxo.
A lista a seguir mostra os métodos mais importantes do descritor de arquivo de um provedor de conteúdo.
Cada um deles tem um método ContentResolver
correspondente com a
string "Descritor" anexada ao nome do método. Por exemplo, o
ContentResolver
análogo de
openAssetFile()
é
openAssetFileDescriptor()
:
-
openTypedAssetFile()
-
Este método retorna um descritor de arquivo de recurso, mas somente se o tipo MIME fornecido for
compatível com o provedor. O autor da chamada (o aplicativo que realiza a colagem) fornece um padrão
de tipo MIME. O provedor de conteúdo (do aplicativo que copiou um URI para a
área de transferência) retorna um identificador de arquivo
AssetFileDescriptor
se puder fornecer esse tipo MIME ou lança uma exceção, se não puder.Esse método lida com subseções de arquivos. Você pode usá-lo para ler recursos que o provedor de conteúdo copiou para a área de transferência.
-
openAssetFile()
-
Este método é uma forma mais geral de
openTypedAssetFile()
. Ele não filtra os tipos MIME permitidos, mas pode ler subseções de arquivos. -
openFile()
-
Esta é uma forma mais geral de
openAssetFile()
. Esse método não pode ler subseções de arquivos.
Opcionalmente, use o método
openPipeHelper()
com o método do descritor de arquivo. Isso permite que o aplicativo de colagem leia
os dados do fluxo em uma linha de execução de segundo plano usando um pipe. Para usar esse método, você precisa implementar a
interface ContentProvider.PipeDataWriter
. Um exemplo disso é
fornecido no aplicativo de amostra Note Pad,
no método openTypedAssetFile()
do
NotePadProvider.java
.
Projetar uma funcionalidade efetiva de copiar e colar
Para projetar uma funcionalidade de copiar e colar efetiva para seu aplicativo, lembre-se destes pontos:
- A qualquer momento, há apenas uma transferência na área de transferência. Uma nova operação de cópia de qualquer aplicativo no sistema substitui a transferência anterior. Como o usuário pode navegar para fora do seu aplicativo e fazer uma cópia antes de retornar, não se pode presumir que a área de transferência contém a transferência copiada anteriormente no seu aplicativo.
-
O objetivo de haver vários objetos
ClipData.Item
de transferência é oferecer compatibilidade com a opção de copiar e colar várias seleções, em vez de diferentes formas de referência a uma única seleção. Geralmente, o ideal é que todos os objetosClipData.Item
de uma transferência tenham a mesma forma, ou seja, que todos sejam texto simples, URI de conteúdo ouIntent
, mas não uma mistura. -
Ao fornecer dados, você pode oferecer diferentes representações MIME. Adicione os tipos MIME
aceitos ao
ClipDescription
e, em seguida, implemente os tipos MIME no seu provedor de conteúdo. -
Quando você recebe dados da área de transferência, seu aplicativo é responsável por verificar os
tipos MIME disponíveis e decidir qual deles usar. Mesmo que haja
dados na área de transferência e o usuário solicitar uma colagem, seu aplicativo não será obrigado
a fazer a colagem. Você precisará fazer a colagem se o tipo MIME for compatível. Você
pode transformar os dados da área de transferência em texto usando
coerceToText()
, se quiser. Caso seu aplicativo seja compatível com mais de um dos tipos MIME disponíveis, você poderá permitir que o usuário escolha qual deles usar.