Copiar e colar

O framework baseado na área de transferência do Android para copiar e colar oferece suporte a tipos de dados primitivos e complexos, incluindo:

  • Strings de texto
  • Estruturas de dados complexas
  • Dados de texto e fluxo binário
  • Recursos de aplicativos

Dados de texto simples são armazenados diretamente na área de transferência, enquanto dados complexos são armazenados como uma referência que o aplicativo de colagem resolve com um provedor de conteúdo.

A cópia e a colagem funcionam em um aplicativo e entre aplicativos que implementam o framework.

Como parte do framework usa provedores de conteúdo, este documento pressupõe certa familiaridade com a API Android Content Provider.

Trabalhar com texto

Alguns componentes oferecem suporte à cópia e colagem de texto, conforme mostrado na tabela a seguir.

Componente Copiando texto Colando texto
BasicTextField ✅ ✅
TextField ✅ ✅
SelectionContainer ✅

Por exemplo, você pode copiar o texto no card para a área de transferência no snippet a seguir e colar o texto copiado no TextField. Mostre o menu para colar o texto tocando e mantendo pressionado o TextField ou tocando no marcador do cursor.

val textFieldState = rememberTextFieldState()

Column {
    Card {
        SelectionContainer {
            Text("You can copy this text")
        }
    }
    BasicTextField(state = textFieldState)
}

Você pode colar o texto com o seguinte atalho de teclado: Ctrl+V . O atalho de teclado também está disponível por padrão. Consulte Processar ações do teclado para mais detalhes.

Copiar com ClipboardManager

Você pode copiar textos para a área de transferência com ClipboardManager. O método setText() copia o objeto AnnotatedString transmitido para a área de transferência. O snippet a seguir copia "Hello, clipboard" para a área de transferência quando o usuário clica no botão.

// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current

Button(
    onClick = {
        // Copy "Hello, clipboard" to the clipboard
        clipboardManager.setText(AnnotatedString("Hello, clipboard"))
    }
) {
    Text("Click to copy a text")
}

O snippet a seguir faz a mesma coisa, mas oferece um controle mais granular. Um caso de uso comum é a cópia de conteúdo sensível, como senhas. ClipEntry descreve um item na área de transferência. Ele contém um ClipData objeto que descreve os dados na área de transferência. ClipData.newPlainText() método é um método de conveniência para criar um objeto ClipData de um objeto String. Você pode definir o objeto ClipEntry criado na área de transferência chamando o método setClip() no objeto ClipboardManager.

// Retrieve a ClipboardManager object
val clipboardManager = LocalClipboardManager.current

Button(
    onClick = {
        val clipData = ClipData.newPlainText("plain text", "Hello, clipboard")
        val clipEntry = ClipEntry(clipData)
        clipboardManager.setClip(clipEntry)
    }
) {
    Text("Click to copy a text")
}

Colar com ClipboardManager

Você pode acessar o texto copiado para a área de transferência chamando o getText() método no ClipboardManager. O método getText() retorna um objeto AnnotatedString quando um texto é copiado na área de transferência. O snippet a seguir anexa o texto na área de transferência ao texto no TextField.

val textFieldState = rememberTextFieldState()

Column {
    TextField(state = textFieldState)

    Button(
        onClick = {
            // The getText method returns an AnnotatedString object or null
            val annotatedString = clipboardManager.getText()
            if (annotatedString != null) {
                // The pasted text is placed on the tail of the TextField
                textFieldState.edit {
                    append(annotatedString.text)
                }
            }
        }
    ) {
        Text("Click to paste the text in the clipboard")
    }
}

Trabalhar com conteúdo avançado

Os usuários adoram imagens, vídeos e outros conteúdos expressivos. Seu app pode permitir que o usuário copie conteúdo avançado com ClipboardManager e ClipEntry. O modificador contentReceiver ajuda a implementar a colagem de conteúdo avançado.

Copiar conteúdo avançado

Seu app não pode copiar conteúdo avançado diretamente para a área de transferência. Em vez disso, seu app transmite um objeto URI para a área de transferência e fornece acesso ao conteúdo com um ContentProvider. O snippet de código a seguir mostra como copiar uma imagem JPEG para a área de transferência. Consulte Copiar fluxos de dados para mais detalhes.

// Get a reference to the context
val context = LocalContext.current

Button(
    onClick = {
        // URI of the copied JPEG data
        val uri = Uri.parse("content://your.app.authority/0.jpg")
        // Create a ClipData object from the URI value
        // A ContentResolver finds a proper ContentProvider so that ClipData.newUri can set appropriate MIME type to the given URI
        val clipData = ClipData.newUri(context.contentResolver, "Copied", uri)
        // Create a ClipEntry object from the clipData value
        val clipEntry = ClipEntry(clipData)
        // Copy the JPEG data to the clipboard
        clipboardManager.setClip(clipEntry)
    }
) {
    Text("Copy a JPEG data")
}

Colar um conteúdo avançado

Com o modificador contentReceiver, você pode processar a colagem de conteúdo avançado em BasicTextField no componente modificado. O snippet de código a seguir adiciona o URI colado de um dado de imagem a uma lista de Uri objetos.

// A URI list of images
val imageList = remember { mutableStateListOf<Uri>() }

// Remember the ReceiveContentListener object as it is created inside a Composable scope
val receiveContentListener = remember {
    ReceiveContentListener { transferableContent ->
        // Handle the pasted data if it is image data
        when {
            // Check if the pasted data is an image or not
            transferableContent.hasMediaType(MediaType.Image) -> {
                // Handle for each ClipData.Item object
                // The consume() method returns a new TransferableContent object containging ignored ClipData.Item objects
                transferableContent.consume { item ->
                    val uri = item.uri
                    if (uri != null) {
                        imageList.add(uri)
                    }
                    // Mark the ClipData.Item object consumed when the retrieved URI is not null
                    uri != null
                }
            }
            // Return the given transferableContent when the pasted data is not an image
            else -> transferableContent
        }
    }
}

val textFieldState = rememberTextFieldState()

BasicTextField(
    state = textFieldState,
    modifier = Modifier
        .contentReceiver(receiveContentListener)
        .fillMaxWidth()
        .height(48.dp)
)

O modificador contentReceiver usa um objeto ReceiveContentListener como argumento e chama o método onReceive do objeto transmitido quando o usuário cola dados no BasicTextField dentro do componente modificado.

Um objeto TransferableContent é transmitido para o método onReceive, que descreve os dados que podem ser transferidos entre apps colando nesse caso. Você pode acessar o objeto ClipEntry consultando o atributo clipEntry.

Um ClipEntry objeto pode ter vários ClipData.Item objetos quando o usuário seleciona várias imagens e as copia para a área de transferência por exemplo. Marque consumido ou ignorado para cada objeto ClipData.Item e retorne um TransferableContent contendo os objetos ClipData.Item ignorados para que o modificador contentReceiver ancestral mais próximo possa recebê-lo.

O método TransferableContent.hasMediaType() pode ajudar a determinar se o objeto TransferableContent pode fornecer um item com o tipo de mídia. Por exemplo, a chamada de método a seguir retorna true se o objeto TransferableContent puder fornecer uma imagem.

transferableContent.hasMediaType(MediaType.Image)

Trabalhar com dados complexos

Você pode copiar dados complexos para a área de transferência da mesma maneira que faz para o conteúdo avançado. Consulte Usar provedores de conteúdo para copiar dados complexos para mais detalhes.

Você também pode processar as colagens de dados complexos da mesma maneira que para o conteúdo avançado. Você pode receber um URI dos dados colados. Os dados reais podem ser recuperados de um ContentProvider. Consulte Recuperar dados do provedor para mais informações.

Feedback para copiar 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 Android mostra uma interface padrão quando usuários executam ações de cópia no Android 13 (nível da API 33) e versões mais recentes. Devido a esse recurso, há risco de notificação duplicada. Saiba mais sobre esse caso extremo em Evitar notificações duplicadas.

Animação mostrando a notificação da área de transferência do Android 13
Figura 1. Interface mostrada quando o conteúdo entra na área de transferência no Android 13 e versões mais recentes.

Envie feedback manualmente aos usuários que executam ações de cópia no Android 12L (nível 32 da API) e versões anteriores. Consulte a recomendação.

Conteúdo sensível

Se você escolher que seu app permita que o usuário copie conteúdo sensível para a área de transferência, como senhas, seu app precisa informar o sistema para que ele possa evitar mostrar o conteúdo sensível copiado na interface (Figura 2).

Visualização do texto copiado com sinalização de conteúdo sensível.
Figura 2. Visualização do texto copiado com um marcador de conteúdo sensível.

Adicione um marcador a ClipDescription em ClipData antes de chamar o método setClip() no objeto ClipboardManager:

// 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)
    }
}