Introduction
- Presse-papier : zone transitoire de stockage contenant un élément à la fois
- Deux opérations supportées : copy et paste
-
Types de données stockables dans le presse-papier :
- Texte brut
- URI (utilisation de getContentResolver() pour récupérer le type MIME de la ressource ainsi que son contenu)
- Intent (raccourci vers une activité)
Confidentialité du presse-papier
-
Android < 10 : possibilité de lire le presse-papier depuis toute application, y compris depuis un service en arrière-plan
- Avantage : permet de créer des applications de gestion de presse-papier avec conservation d'historique
-
Inconvénient : autorise des applications malveillantes à espionner le contenu du presse-papier (pouvant contenir des données sensibles comme des mots de passe)
- Android ≥ 10 : possibilité de lire le presse-papier que pour une activité ayant le focus
- Permission android.permission.READ_CLIPBOARD accordée par défaut à toutes les applications
- Possibilité de supprimer cette permission pour une application avec adb en suivant ce processus
Élément dans le presse-papier
-
ClipData représente des données dans le presse-papier
- ClipDescription ClipData.getDescription() : métadonnées avec les types MIME représentés
-
Un ClipData contient généralement un ClipData.Item (getItemAt(0)) qui peut être :
- Du texte pur (ou du texte HTML) : ClipData.new{Plain, Html}Text(CharSequence label, CharSequence text) ; ClipData.Item.get{Plain, Html}Text()
- Une URI : ClipData.newUri(ContentResolver resolver, CharSequence label, Uri uri) ; existe aussi en version newRawUri sans spécifier le résolver ; ClipData.Item.getUri()
- Un Intent : ClipData.newIntent(CharSequence label, Intent intent)
Fonctionnement du presse-papier
-
Récupération du ClipboardManager :
- ClipboardManager cbm = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE)
- Accès au presse-papier possible uniquement depuis l'application ayant le focus
- Création du nouveau ClipData (avec texte, URI ou Intent)
- Copie dans le presse-papier : cbm.setPrimaryClip(myClipData) ;
- ...
-
Récupération de l'item principal pour le coller ailleurs (on consulte ses getters) : ClipData.Item item = myClipData.getItemAt(0)
- Si une URI est présente, on utilise le ContentResolver pour récupérer les données et les mettre sous une forme adaptée
- On peut essayer de forcer la conversion en texte de tout type de données avec ClipData.Item.coerceToText(Context)
Observation du presse-papier
- Possibilité d'observer des changements dans le presse-papier en ajoutant un listener
OnPrimaryClipChangedListener listener = new OnPrimaryClipChangedListener() {
public void onPrimaryClipChanged() {
doSomething();
}
};
((ClipboardManager) getSystemService(CLIPBOARD_SERVICE)).addPrimaryClipChangedListener(listener);
Exemple : une application de chiffrement du presse-papier
- Récupération du texte dans le presse-papier avec le bouton Paste et copie du texte dans l'EditText
- Chiffrement et déchiffrement possible en AES 256 avec les boutons Encrypt et Decrypt
- Possibilité de transférer dans le presse-papier le contenu de l'EditText avec le bouton Copy
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License
package fr.upem.coursand.clipboard
import android.Manifest
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import fr.upem.coursand.R
import kotlinx.android.synthetic.main.activity_encrypt_clipboard.*
/** An activity that encrypt/decrypt the text content of the clipboard */
class EncryptClipboardActivity : AppCompatActivity() {
private val clipboardManager: ClipboardManager
by lazy { getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_encrypt_clipboard)
}
/** Paste text from the clipboard */
fun onPasteClick(v: View) {
if (clipboardManager.hasPrimaryClip()) {
val item = clipboardManager.primaryClip?.getItemAt(0)
val text = item?.coerceToText(this)
if (text != null)
clipboardView.setText(text)
}
}
/** Copy text to the clipboard */
fun onCopyClick(v: View) {
val clip = ClipData.newPlainText("text", clipboardView.text)
clipboardManager.setPrimaryClip(clip)
}
/** Encrypt text */
fun onEncryptClick(v: View) {
// we assume encryption and decryption is fast and can be done directly in UI thread
val encrypted = clipboardView.text.toString().encrypt(passphraseView.text.toString())
clipboardView.setText(encrypted)
}
/** Decrypt text */
fun onDecryptClick(v: View) {
val decrypted = clipboardView.text.toString().decrypt(passphraseView.text.toString())
if (decrypted == null)
Toast.makeText(this, "Decryption failure: is the passphrase correct?", Toast.LENGTH_SHORT).show()
else
clipboardView.setText(decrypted)
}
}