कॉपी करें और चिपकाएं

कॉपी और पेस्ट करने के लिए, Android के क्लिपबोर्ड पर आधारित फ़्रेमवर्क में, ये डेटा टाइप इस्तेमाल किए जा सकते हैं:

  • टेक्स्ट स्ट्रिंग
  • कॉम्प्लेक्स डेटा स्ट्रक्चर
  • टेक्स्ट और बाइनरी स्ट्रीम डेटा
  • ऐप्लिकेशन एसेट

सादा टेक्स्ट डेटा सीधे क्लिपबोर्ड में सेव किया जाता है. वहीं, कॉम्प्लेक्स डेटा को रेफ़रंस के तौर पर सेव किया जाता है. इसे पेस्ट करने वाला ऐप्लिकेशन, कॉन्टेंट देने वाली कंपनी की मदद से हल करता है.

कॉपी और पेस्ट करने की सुविधा, किसी ऐप्लिकेशन के अंदर और फ़्रेमवर्क को लागू करने वाले ऐप्लिकेशन के बीच, दोनों में काम करती है.

फ़्रेमवर्क का कुछ हिस्सा, कॉन्टेंट देने वाली कंपनियों का इस्तेमाल करता है. इसलिए, इस दस्तावेज़ में यह मान लिया गया है कि आपको Android Content Provider API के बारे में थोड़ी जानकारी है.

टेक्स्ट के साथ काम करना

कुछ कॉम्पोनेंट, टेक्स्ट को कॉपी और पेस्ट करने की सुविधा देते हैं. इनके बारे में यहां दी गई टेबल में बताया गया है.

कॉम्पोनेंट टेक्स्ट कॉपी करना टेक्स्ट पेस्ट करना
BasicTextField ✅ ✅
TextField ✅ ✅
SelectionContainer ✅

उदाहरण के लिए, यहां दिए गए स्निपेट में, कार्ड में मौजूद टेक्स्ट को क्लिपबोर्ड पर कॉपी किया जा सकता है. इसके बाद, कॉपी किए गए टेक्स्ट को TextField में पेस्ट किया जा सकता है. TextField पर टच करके दबाए रखने या कर्सर हैंडल पर टैप करके, टेक्स्ट को पेस्ट करने के लिए मेन्यू दिखाया जा सकता है.

val textFieldState = rememberTextFieldState()

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

टेक्स्ट को पेस्ट करने के लिए, इस कीबोर्ड शॉर्टकट का इस्तेमाल किया जा सकता है: Ctrl+V . यह कीबोर्ड शॉर्टकट, डिफ़ॉल्ट रूप से भी उपलब्ध होता है. ज़्यादा जानकारी के लिए, कीबोर्ड की कार्रवाइयों को मैनेज करना लेख पढ़ें.

ClipboardManager की मदद से कॉपी करना

ClipboardManager की मदद से, टेक्स्ट को क्लिपबोर्ड पर कॉपी किया जा सकता है. इसके setText() तरीके से, पास किए गए AnnotatedString ऑब्जेक्ट को क्लिपबोर्ड पर कॉपी किया जाता है. यहां दिए गए स्निपेट में, जब उपयोगकर्ता बटन पर क्लिक करता है, तो "Hello, clipboard" को क्लिपबोर्ड पर कॉपी किया जाता है.

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

यहां दिए गए स्निपेट में भी यही काम किया जाता है, लेकिन इससे आपको ज़्यादा कंट्रोल मिलता है. इसका एक सामान्य उदाहरण, पासवर्ड जैसे संवेदनशील कॉन्टेंट को कॉपी करना है. ClipEntry से, क्लिपबोर्ड पर मौजूद किसी आइटम के बारे में पता चलता है. इसमें एक ClipData ऑब्जेक्ट होता है, जिससे क्लिपबोर्ड पर मौजूद डेटा के बारे में पता चलता है. ClipData.newPlainText() तरीका, String ऑब्जेक्ट से ClipData ऑब्जेक्ट बनाने का एक आसान तरीका है. बनाए गए ClipEntry ऑब्जेक्ट को क्लिपबोर्ड पर सेट किया जा सकता है ClipboardManager ऑब्जेक्ट पर setClip() तरीके को कॉल करके.

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

ClipboardManager की मदद से पेस्ट करना

`ClipboardManager` पर getText() तरीके को कॉल करके, क्लिपबोर्ड पर कॉपी किए गए टेक्स्ट को ऐक्सेस किया जा सकता है. जब किसी टेक्स्ट को क्लिपबोर्ड पर कॉपी किया जाता है, तो इसका getText() तरीका, AnnotatedString ऑब्जेक्ट दिखाता है. यहां दिए गए स्निपेट में, क्लिपबोर्ड पर मौजूद टेक्स्ट को 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")
    }
}

रिच कॉन्टेंट के साथ काम करना

लोगों को इमेज, वीडियो, और अन्य एक्सप्रेसिव कॉन्टेंट पसंद होता है. आपका ऐप्लिकेशन, ClipboardManager और ClipEntry की मदद से, लोगों को रिच कॉन्टेंट कॉपी करने की सुविधा दे सकता है. contentReceiver मॉडिफ़ायर की मदद से, रिच कॉन्टेंट को पेस्ट करने की सुविधा लागू की जा सकती है.

रिच कॉन्टेंट कॉपी करना

आपका ऐप्लिकेशन, रिच कॉन्टेंट को सीधे क्लिपबोर्ड पर कॉपी नहीं कर सकता. इसके बजाय, आपका ऐप्लिकेशन क्लिपबोर्ड पर एक URI ऑब्जेक्ट पास करता है और ContentProvider की मदद से कॉन्टेंट को ऐक्सेस करने की सुविधा देता है. यहां दिए गए कोड स्निपेट में, JPEG इमेज को क्लिपबोर्ड पर कॉपी करने का तरीका बताया गया है. ज़्यादा जानकारी के लिए, डेटा स्ट्रीम कॉपी करना लेख पढ़ें.

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

रिच कॉन्टेंट पेस्ट करना

contentReceiver मॉडिफ़ायर की मदद से, बदले गए कॉम्पोनेंट में BasicTextField पर रिच कॉन्टेंट को पेस्ट किया जा सकता है. यहां दिए गए कोड स्निपेट में, इमेज डेटा के पेस्ट किए गए यूआरआई को Uri ऑब्जेक्ट की सूची में जोड़ा जाता है.

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

contentReceiver मॉडिफ़ायर, ReceiveContentListener ऑब्जेक्ट को अपने आर्ग्युमेंट के तौर पर लेता है. साथ ही, जब उपयोगकर्ता बदले गए कॉम्पोनेंट में BasicTextField पर डेटा पेस्ट करता है, तो पास किए गए ऑब्जेक्ट के onReceive तरीके को कॉल करता है.

onReceive तरीके में एक TransferableContent ऑब्जेक्ट पास किया जाता है. इससे उस डेटा के बारे में पता चलता है जिसे ऐप्लिकेशन के बीच ट्रांसफ़र किया जा सकता है जिसे इस मामले में पेस्ट करके ट्रांसफ़र किया जा सकता है. clipEntry एट्रिब्यूट को रेफ़र करके, ClipEntry ऑब्जेक्ट को ऐक्सेस किया जा सकता है.

जब उपयोगकर्ता कई इमेज चुनता है और उन्हें क्लिपबोर्ड पर कॉपी करता है, तो ClipEntry ऑब्जेक्ट में कई ClipData.Item ऑब्जेक्ट हो सकते हैं उदाहरण के लिए. आपको हर ClipData.Item ऑब्जेक्ट के लिए, इस्तेमाल किया गया या अनदेखा किया गया मार्क करना चाहिए. साथ ही, अनदेखा किए गए ClipData.Item ऑब्जेक्ट वाला TransferableContent दिखाना चाहिए, ताकि सबसे नज़दीकी पैरंट contentReceiver मॉडिफ़ायर इसे पा सके.

TransferableContent.hasMediaType() तरीके से, यह पता लगाया जा सकता है कि TransferableContent ऑब्जेक्ट, मीडिया टाइप वाला कोई आइटम दे सकता है या नहीं. उदाहरण के लिए, अगर TransferableContent ऑब्जेक्ट कोई इमेज दे सकता है, तो यहां दिए गए तरीके को कॉल करने पर true मिलता है.

transferableContent.hasMediaType(MediaType.Image)

कॉम्प्लेक्स डेटा के साथ काम करना

कॉम्प्लेक्स डेटा को क्लिपबोर्ड पर उसी तरह कॉपी किया जा सकता है जिस तरह रिच कॉन्टेंट को कॉपी किया जाता है. ज़्यादा जानकारी के लिए, कॉम्प्लेक्स डेटा कॉपी करने के लिए कॉन्टेंट देने वाली कंपनियों का इस्तेमाल करना लेख पढ़ें.

रिच कॉन्टेंट की तरह ही, कॉम्प्लेक्स डेटा को पेस्ट करने की सुविधा को भी मैनेज किया जा सकता है. पेस्ट किए गए डेटा का यूआरआई पाया जा सकता है. असल डेटा को ContentProvider से वापस पाया जा सकता है. ज़्यादा जानकारी के लिए, सेवा देने वाली कंपनी से डेटा वापस पाना लेख पढ़ें.

कॉन्टेंट कॉपी करने के बारे में सुझाव/राय देना या शिकायत करना

जब लोग कॉन्टेंट को क्लिपबोर्ड पर कॉपी करते हैं, तो उन्हें फ़ीडबैक मिलता है. इसलिए, Android 13 (एपीआई लेवल 33) और इसके बाद के वर्शन में, Android, लोगों को डिफ़ॉल्ट यूज़र इंटरफ़ेस (यूआई) दिखाता है. यह यूआई, कॉपी और पेस्ट करने की सुविधा देने वाले फ़्रेमवर्क के अलावा दिखता है. इस सुविधा की वजह से, डुप्लीकेट सूचनाएं दिखने का खतरा होता है. इस बारे में ज़्यादा जानने के लिए, डुप्लीकेट सूचनाएं दिखने से बचना लेख पढ़ें.

Android 13 के क्लिपबोर्ड की सूचना दिखाने वाला ऐनिमेशन
पहली इमेज. Android 13 और इसके बाद के वर्शन में, जब कॉन्टेंट क्लिपबोर्ड पर कॉपी होता है, तो यह यूआई दिखता है.

Android 12L (एपीआई लेवल 32) और इससे पहले के वर्शन में, कॉन्टेंट कॉपी करने पर लोगों को मैन्युअल तरीके से फ़ीडबैक दें. सुझाव देखें.

संवेदनशील कॉन्टेंट

अगर आपने अपने ऐप्लिकेशन में, लोगों को पासवर्ड जैसे संवेदनशील कॉन्टेंट को क्लिपबोर्ड पर कॉपी करने की सुविधा दी है, तो आपके ऐप्लिकेशन को सिस्टम को इसकी जानकारी देनी होगी. इससे सिस्टम, कॉपी किए गए संवेदनशील कॉन्टेंट को यूआई में नहीं दिखाएगा (दूसरी इमेज).

कॉपी किए गए टेक्स्ट की झलक, जिसमें संवेदनशील कॉन्टेंट को फ़्लैग किया गया है.
दूसरी इमेज. संवेदनशील कॉन्टेंट के फ़्लैग के साथ, कॉपी किए गए टेक्स्ट की झलक.

ClipboardManager ऑब्जेक्ट पर setClip() तरीका कॉल करने से पहले, आपको ClipData में ClipDescription में एक फ़्लैग जोड़ना होगा:

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