Kopyala ve yapıştır

Android, kopyalama ve yapıştırma için güçlü bir panosu temel alan çerçeve sunar. Metin dizeleri, karmaşık veri yapıları, metin ve ikili akış verileri ve uygulama öğeleri dahil olmak üzere basit ve karmaşık veri türlerini destekler. Basit metin verileri doğrudan panoda depolanır. Karmaşık veriler ise yapıştırma uygulamasının bir içerik sağlayıcıyla çözdüğü bir referans olarak depolanır. Kopyalama ve yapıştırma, hem bir uygulama içinde hem de çerçeveyi uygulayan uygulamalar arasında çalışır.

Çerçevenin bir kısmı içerik sağlayıcıları kullandığından bu dokümanda, İçerik sağlayıcılar bölümünde açıklanan Android Content Provider API hakkında bilgi sahibi olduğunuz varsayılmaktadır.

Kullanıcılar, içeriği panosuna kopyalarken geri bildirim bekler. Bu nedenle Android, kopyalama ve yapıştırma işlemini destekleyen çerçeveye ek olarak Android 13 (API düzeyi 33) ve sonraki sürümlerde kopyalama işlemi sırasında kullanıcılara varsayılan bir kullanıcı arayüzü gösterir. Bu özellik nedeniyle yinelenen bildirim alma riski vardır. Bu uç durum hakkında daha fazla bilgiyi Yinelenen bildirimlerden kaçınma bölümünde bulabilirsiniz.

Android 13 panosu bildirimini gösteren animasyon
Şekil 1. Android 13 ve sonraki sürümlerde içerik panoya girdiğinde gösterilen kullanıcı arayüzü.

Android 12L (API düzeyi 32) ve önceki sürümlerde kopyalama yaparken kullanıcılara manuel olarak geri bildirim sağlayın. Bu dokümanda bu konudaki önerilere göz atın.

Pano çerçevesi

Pano çerçevesini kullandığınızda verileri bir klip nesnesine, ardından klip nesnesini sistem genelindeki panosuna yerleştirin. Klip nesnesi üç biçimden birinde olabilir:

Metin
Metin dizesi. Dize doğrudan klip nesnesine yerleştirilir ve daha sonra panosuna kopyalanır. Dizesi yapıştırmak için panodan klip nesnesini alın ve dizeyi uygulamanızın depolama alanına kopyalayın.
URI
URI biçimini temsil eden bir Uri nesnesi. Bu, esas olarak bir içerik sağlayıcıdan karmaşık verilerin kopyalanması içindir. Verileri kopyalamak için bir Uri nesnesini bir klip nesnesine, klip nesnesini de panoya yerleştirin. Verileri yapıştırmak için klip nesnesini, Uri nesnesini alın, bunu bir veri kaynağına (ör. içerik sağlayıcı) çözümleyin ve kaynaktaki verileri uygulamanızın depolama alanına kopyalayın.
Amaç
Intent. Bu, uygulama kısayollarının kopyalanmasını destekler. Verileri kopyalamak için bir Intent oluşturun, bu nesneyi bir klip nesnesine koyun ve klip nesnesini panoya yerleştirin. Verileri yapıştırmak için klip nesnesini alın ve ardından Intent nesnesini uygulamanızın bellek alanına kopyalayın.

Panoda aynı anda yalnızca bir klip nesnesi bulunur. Bir uygulama panoya bir klip nesnesi yerleştirdiğinde, önceki klip nesnesi kaybolur.

Kullanıcıların uygulamanıza veri yapıştırmasına izin vermek istiyorsanız her tür veriyi işlemeniz gerekmez. Kullanıcılara yapıştırma seçeneği sunmadan önce, panosundaki verileri inceleyebilirsiniz. Klip nesnesi, belirli bir veri formuna sahip olmanın yanı sıra hangi MIME türlerinin kullanılabileceğini belirten meta veriler de içerir. Bu meta veriler, uygulamanızın panosundaki verilerle yararlı bir şey yapıp yapamayacağını belirlemenize yardımcı olur. Örneğin, temel olarak metinle çalışan bir uygulamanız varsa URI veya intent içeren klip nesnelerini yoksayabilirsiniz.

Ayrıca, kullanıcıların, verileri panoya kopyaladıktan sonra biçimi ne olursa olsun metin yapıştırmasına izin verebilirsiniz. Bunu yapmak için, pano verilerini metin olarak temsil etmeye zorlayıp bu metni yapıştırın. Bu konu Panoyu metne zorlama bölümünde açıklanmıştır.

Pano sınıfları

Bu bölümde, panos çerçevesi tarafından kullanılan sınıflar açıklanmaktadır.

Pano Yöneticisi

Android sistem panosu, genel ClipboardManager sınıfı tarafından temsil edilir. Bu sınıfı doğrudan örneklendirmeyin. Bunun yerine, getSystemService(CLIPBOARD_SERVICE) komutunu çağırarak ona referans alın.

ClipData, ClipData.Item ve ClipDescription

Verileri panosuna eklemek için verilerin açıklamasını ve verilerin kendisini içeren bir ClipData nesnesi oluşturun. Panoda aynı anda bir ClipData bulunabilir. ClipData, bir ClipDescription nesnesi ve bir veya daha fazla ClipData.Item nesnesi içerir.

Bir ClipDescription nesnesi, kliple ilgili meta verileri içerir. Özellikle, klibin verileri için kullanılabilen MIME türlerinin bir dizisini içerir. Ayrıca, Android 12 (API düzeyi 31) ve sonraki sürümlerde meta veriler, nesnenin stilize metin içerip içermediği ve nesnedeki metin türü hakkında bilgi içerir. Bir klibi panosuna eklediğinizde bu bilgiler, klibi yapıştıran uygulamalar tarafından kullanılabilir. Bu uygulamalar, klibi işleyip işleyemeyeceklerini inceleyebilir.

ClipData.Item nesnesi metni, URI'yi veya intent verilerini içerir:

Metin
A CharSequence.
URI
A Uri. Bu genellikle bir içerik sağlayıcı URI'si içerir ancak herhangi bir URI'ye izin verilir. Verileri sağlayan uygulama, URI'yı panoya yerleştirir. Verileri yapıştırmak isteyen uygulamalar, URI'yi panosundan alır ve içerik sağlayıcıya veya başka bir veri kaynağına erişip verileri almak için kullanır.
Amaç
Intent. Bu veri türü, uygulama kısayollarını panoya kopyalamanıza olanak tanır. Kullanıcılar daha sonra kullanmak üzere kısayolu uygulamalarına yapıştırabilir.

Bir klibe birden fazla ClipData.Item nesnesi ekleyebilirsiniz. Bu sayede kullanıcılar birden fazla seçimi tek bir klip olarak kopyalayıp yapıştırabilir. Örneğin, kullanıcının aynı anda birden fazla öğe seçmesine olanak tanıyan bir liste widget'ınız varsa tüm öğeleri bir kerede panoya kopyalayabilirsiniz. Bunu yapmak için her liste öğesi için ayrı bir ClipData.Item oluşturun ve ardından ClipData.Item nesnelerini ClipData nesnesine ekleyin.

ClipData kolaylık yöntemleri

ClipData sınıfı, tek bir ClipData.Item nesnesi ve basit bir ClipDescription nesnesi içeren bir ClipData nesnesi oluşturmak için statik kolaylık yöntemleri sağlar:

newPlainText(label, text)
Tek ClipData.Item nesnesi metin dizesi içeren bir ClipData nesnesi döndürür. ClipDescription nesnesinin etiketi label olarak ayarlanmıştır. ClipDescription dosyasında tek MIME türü MIMETYPE_TEXT_PLAIN'dur.

Metin dizesinden klip oluşturmak için newPlainText() simgesini kullanın.

newUri(resolver, label, URI)
Tek ClipData.Item nesnesi URI içeren bir ClipData nesnesi döndürür. ClipDescription nesnesinin etiketi label olarak ayarlanmıştır. URI bir içerik URI'siyse (yani Uri.getScheme() content: döndürüyorsa) yöntem, mevcut MIME türlerini içerik sağlayıcıdan almak için resolver içinde sağlanan ContentResolver nesnesini kullanır. Daha sonra bunları ClipDescription konumunda depolar. content: URI olmayan bir URI için yöntem, MIME türünü MIMETYPE_TEXT_URILIST olarak ayarlar.

Özellikle content: URI'si olmak üzere bir URI'dan klip oluşturmak için newUri() kullanın.

newIntent(label, intent)
Tek ClipData.Item nesnesi bir Intent içeren ClipData nesnesini döndürür. ClipDescription nesnesinin etiketi label olarak ayarlanmıştır. MIME türü MIMETYPE_TEXT_INTENT olarak ayarlanmıştır.

Intent nesnesinden klip oluşturmak için newIntent()'ü kullanın.

Panoya kopyalanan verileri metne dönüştürme

Uygulamanız yalnızca metinle çalışıyor olsa bile ClipData.Item.coerceToText() yöntemiyle dönüştürerek metin olmayan verileri panodan kopyalayabilirsiniz.

Bu yöntem, ClipData.Item içindeki verileri metne dönüştürür ve CharSequence döndürür. ClipData.Item.coerceToText() işlevinin döndürdüğü değer, ClipData.Item'teki verilerin biçimine bağlıdır:

Metin
ClipData.Item metinse (yani getText() null değilse) coerceToText() metni döndürür.
URI
ClipData.Item bir URI ise (yani getUri() boş değilse) coerceToText() bunu içerik URI'si olarak kullanmaya çalışır.
  • URI bir içerik URI'siyse ve sağlayıcı bir metin akışı döndürebiliyorsa coerceToText() bir metin akışı döndürür.
  • URI bir içerik URI'siyse ancak sağlayıcı metin akışı sunmuyorsa coerceToText(), URI'nin temsilini döndürür. Temsil, Uri.toString() tarafından döndürülenle aynıdır.
  • URI bir içerik URI'si değilse coerceToText(), URI'nin bir temsilini döndürür. Temsil, Uri.toString() tarafından döndürülen değerle aynıdır.
Amaç
ClipData.Item bir Intent ise (yani getIntent() boş değilse) coerceToText() bunu bir Intent URI'sine dönüştürür ve döndürür. Temsil, Intent.toUri(URI_INTENT_SCHEME) tarafından döndürülen temsille aynıdır.

Panoya çerçevesi Şekil 2'de özetlenmiştir. Bir uygulama, verileri kopyalamak için ClipboardManager genel panosuna bir ClipData nesnesi yerleştirir. ClipData, bir veya daha fazla ClipData.Item nesnesi ve bir ClipDescription nesnesi içerir. Bir uygulama, verileri yapıştırmak için ClipData öğesini, MIME türünü ClipDescription kaynağından ve verileri ClipData.Item veya ClipData.Item tarafından referans verilen içerik sağlayıcıdan alır.

Kopyalama ve yapıştırma çerçevesinin blok diyagramını gösteren resim
Şekil 2. Android panosu çerçevesi.

Panoya kopyala

Verileri panoya kopyalamak için global ClipboardManager nesnesinin bir tutamacını alın, bir ClipData nesnesi oluşturun ve bu nesneye bir ClipDescription ve bir veya daha fazla ClipData.Item nesnesi ekleyin. Ardından, tamamlanmış ClipData nesnesini ClipboardManager nesnesine ekleyin. Bu, aşağıdaki prosedürde daha ayrıntılı olarak açıklanmaktadır:

  1. Verileri içerik URI'si kullanarak kopyalıyorsanız bir içerik sağlayıcı ayarlayın.
  2. Sistem panosunu alın:

    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);
  3. Verileri yeni bir ClipData nesnesine kopyalayın:

    • Metinler için

      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!");
    • URI için

      Bu snippet, sağlayıcının içerik URI'sine bir kayıt kimliği kodlayarak bir URI oluşturur. Bu teknik, URI'de tanımlayıcı kodlama bölümünde daha ayrıntılı olarak ele alınmıştır.

      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);
    • Intent için

      Bu snippet, bir uygulama için Intent oluşturur ve ardından bunu klip nesnesine yerleştirir:

      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);
  4. Yeni klip nesnesini panoya yerleştirin:

    Kotlin

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip)

    Java

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip);

Panoya kopyalarken geri bildirim sağlama

Kullanıcılar, bir uygulamanın içeriği panosuna kopyaladığında görsel geri bildirim bekler. Bu işlem, Android 13 ve sonraki sürümlerin yüklü olduğu cihazlardaki kullanıcılar için otomatik olarak yapılır ancak önceki sürümlerde manuel olarak uygulanmalıdır.

Android 13'ten itibaren sistem, panoya içerik eklendiğinde standart bir görsel onay görüntüler. Yeni onay şunları yapar:

  • İçeriğin başarıyla kopyalandığını onaylar.
  • Kopyalanan içeriğin önizlemesini sağlar.

Android 13 panosu bildirimini gösteren animasyon
Şekil 3. Android 13 ve sonraki sürümlerde panoya içerik girdiğinde gösterilen kullanıcı arayüzü.

Android 12L (API düzeyi 32) ve önceki sürümlerde kullanıcılar, içeriği başarıyla kopyalayıp kopyalamadığınızdan veya neyi kopyaladığından emin olmayabilir. Bu özellik, kopyalama işleminden sonra uygulamalar tarafından gösterilen çeşitli bildirimleri standart hale getirir ve kullanıcılara panos üzerinde daha fazla kontrol sunar.

Yinelenen bildirimleri önleme

Android 12L (API düzeyi 32) ve önceki sürümlerde, kullanıcılar başarılı bir şekilde kopyalama işlemi gerçekleştirdiğinde Toast veya Snackbar gibi bir widget kullanarak görsel uygulama içi geri bildirim göndererek kullanıcıları uyarmanızı öneririz.

Bilgilerin yinelenen şekilde gösterilmesini önlemek için Android 13 ve sonraki sürümlerde uygulama içi kopyadan sonra gösterilen pop-up'ları veya bilgi çubuklarını kaldırmanızı önemle tavsiye ederiz.

Uygulama içi kopyadan sonra bilgi çubuğu yayınlayın.
Şekil 4. Android 13'te kopyalama onayı atıştırmalık çubuğunu gösterirseniz kullanıcı yinelenen mesajlar görür.
Uygulama içi kopyadan sonra pop-up gösterin.
Şekil 5. Android 13'te kopyalama onayı kısa mesajı gösteriyorsanız kullanıcı yinelenen mesajlar görür.

Aşağıda bunun nasıl uygulanacağına dair bir örnek verilmiştir:

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

Hassas içeriği panoya ekleme

Uygulamanız, kullanıcıların şifre veya kredi kartı bilgileri gibi hassas içerikleri panoya kopyalamasına izin veriyorsa ClipboardManager.setPrimaryClip() yöntemini çağırmadan önce ClipData uygulamasında ClipDescription öğesine bir işaret eklemeniz gerekir. Bu işaretin eklenmesi, Android 13 ve sonraki sürümlerde kopyalanan içeriğin görsel onayında hassas içeriğin görünmesini engeller.

Hassas içerikleri işaretlemeden kopyalanan metin önizlemesi
Şekil 6. Hassas içerik işareti olmadan kopyalanan metin önizlemesi.
Hassas içeriği işaretleyen kopyalanan metin önizlemesi.
Şekil 7. Hassas içerik işareti içeren kopyalanan metin önizlemesi.

Hassas içeriği işaretlemek için ClipDescription öğesine fazladan bir boole ekleyin. Hedeflenen API düzeyinden bağımsız olarak tüm uygulamalar bunu yapmalıdır.

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

Panodan yapıştır

Daha önce açıklandığı gibi, global pano nesnesini, klip nesnesini alarak, verilerine bakarak ve mümkünse klip nesnesinden verileri kendi depolama alanınıza kopyalayarak panodaki verileri yapıştırın. Bu bölümde, üç tür pano verisinin nasıl yapıştırılacağı ayrıntılı olarak açıklanmaktadır.

Düz metin yapıştırma

Düz metin yapıştırmak için genel panoyu alın ve düz metin döndürüp döndürmediğini doğrulayın. Ardından klip nesnesini alın ve aşağıdaki prosedürde açıklandığı gibi getText() kullanarak metni kendi depolama alanınıza kopyalayın:

  1. getSystemService(CLIPBOARD_SERVICE) kullanarak global ClipboardManager nesnesini alın. Ayrıca, yapıştırılan metni içerecek bir genel değişken tanımlayın:

    Kotlin

    var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    var pasteData: String = ""

    Java

    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    String pasteData = "";
  2. Mevcut etkinlikte "yapıştır" seçeneğini etkinleştirmeniz veya devre dışı bırakmanız gerekip gerekmediğini belirleyin. Apanızın bir klip içerdiğini ve klibin temsil ettiği veri türünü işleyebildiğinizi doğrulayın:

    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);
    }
  3. Verileri panoya kopyalayın. Koddaki bu noktaya yalnızca "yapıştır" menü öğesi etkinse erişilebilir. Bu nedenle panonun düz metin içerdiğini varsayabilirsiniz. Henüz bir metin dizesi mi yoksa düz metne yönlendiren bir URI mi içerdiğini bilmiyorsunuzdur. Aşağıdaki kod snippet'i bunu test eder ancak yalnızca düz metni işleme kodunu gösterir:

    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;
        }
    }

İçerik URI'sinden veri yapıştırma

ClipData.Item nesnesi bir içerik URI'si içeriyorsa ve MIME türlerinden birini işleyebileceğinizi belirlerseniz verileri almak için bir ContentResolver oluşturun ve uygun içerik sağlayıcı yöntemini çağırın.

Aşağıdaki prosedürde, panodaki içerik URI'sine göre bir içerik sağlayıcıdan nasıl veri alınacağı açıklanmaktadır. Uygulamanın kullanabileceği bir MIME türünün sağlayıcıda mevcut olup olmadığını kontrol eder.

  1. MIME türünü içerecek bir genel değişken tanımlayın:

    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";
  2. Global panoyu alın. İçerik sağlayıcıya erişebilmek için bir içerik çözücü de edinin:

    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();
  3. Panodan birincil klibi ve içeriğini URI olarak alın:

    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();
  4. getType(Uri) işlevini çağırarak URI'nin içerik URI'si olup olmadığını test edin. Uri geçerli bir içerik sağlayıcıya işaret etmiyorsa bu yöntem null değerini döndürür.

    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);
  5. İçerik sağlayıcının, uygulamanın anladığı bir MIME türünü destekleyip desteklemediğini test edin. Çalışıyorsa verileri almak için ContentResolver.query() çağrısı yapın. Döndürülen değer bir 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();
                 }
             }
         }
    }

Niyet Yapıştır

Bir intent'i yapıştırmak için önce genel panoyu alın. ClipData.Item nesnesinin Intent içerip içermediğini kontrol edin. Ardından, intent'i kendi depolama alanınıza kopyalamak için getIntent() işlevini çağırın. Aşağıdaki snippet'te bu gösterilmektedir:

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.
}

Uygulamanız panosundaki verilere eriştiğinde gösterilen sistem bildirimi

Android 12 (API düzeyi 31) ve sonraki sürümlerde, uygulamanız getPrimaryClip() çağrısı yaptığında sistem genellikle bir pop-up mesajı gösterir. İletideki metin aşağıdaki biçimi içerir:

APP pasted from your clipboard

Uygulamanız aşağıdakilerden birini yaptığında sistem bir durum mesajı mesajı göstermez:

  • Kendi uygulamanızdan ClipData erişimi
  • Belirli bir uygulamadan ClipData uygulamasına sürekli olarak erişir. Bildirim yalnızca, uygulamanız söz konusu uygulamadaki verilere ilk kez eriştiğinde görünür.
  • Klip nesnesinin meta verilerini alır (ör. getPrimaryClip() yerine getPrimaryClipDescription() çağrısı yaparak).

Karmaşık verileri kopyalamak için içerik sağlayıcıları kullanma

İçerik sağlayıcılar, veritabanı kayıtları veya dosya akışları gibi karmaşık verilerin kopyalanmasını destekler. Verileri kopyalamak için panoya bir içerik URI'si koyun. Ardından, yapıştırma uygulamaları bu URI'yi panosundan alır ve veritabanı verilerini veya dosya akışı tanımlayıcılarını almak için kullanır.

Yapıştırma uygulaması yalnızca verilerinize ait içerik URI'sini içerdiğinden, hangi veri parçasının alınacağını bilmesi gerekir. Bu bilgileri, URI üzerindeki veriler için bir tanımlayıcı kodlayarak veya kopyalamak istediğiniz verileri döndüren benzersiz bir URI sağlayabilirsiniz. Hangi tekniği seçeceğiniz, verilerinizin organizasyonuna bağlıdır.

Aşağıdaki bölümlerde URI'lerin nasıl oluşturulacağı, karmaşık verilerin ve dosya akışlarının nasıl sağlanacağı açıklanmaktadır. Açıklamalarda, içerik sağlayıcı tasarımının genel ilkelerine aşina olduğunuz varsayılmaktadır.

URI'de tanımlayıcıyı kodlama

Verileri URI ile panoya kopyalamak için kullanabileceğiniz yararlı bir teknik, URI'nin kendisinde veriler için bir tanımlayıcı kodlamaktır. Ardından içerik sağlayıcınız, URI'den tanımlayıcıyı alıp verileri almak için kullanabilir. Yapıştırma uygulamasının, tanımlayıcının var olduğunu bilmesi gerekmez. İçerik sağlayıcının, "referansınızı" (URI ve tanımlayıcı) panodan alması, bunu içerik sağlayıcınıza vermesi ve verileri geri alması yeterlidir.

Genellikle bir tanımlayıcının URI'ye kodlanmasını, URI'nin sonuna eklemek suretiyle sağlarsınız. Örneğin, sağlayıcı URI'nizi aşağıdaki dize olarak tanımladığınızı varsayalım:

"content://com.example.contacts"

Bu URI'ye bir ad kodlamak istiyorsanız aşağıdaki kod snippet'ini kullanın:

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

Halihazırda bir içerik sağlayıcı kullanıyorsanız URI'nin kopyalama için olduğunu belirten yeni bir URI yolu ekleyebilirsiniz. Örneğin, şu URI yollarına sahip olduğunuzu varsayalım:

"content://com.example.contacts/people"
"content://com.example.contacts/people/detail"
"content://com.example.contacts/people/images"

URI'leri kopyalamak için başka bir yol ekleyebilirsiniz:

"content://com.example.contacts/copying"

Ardından, kalıp eşleştirme yaparak "kopya" URI'sini algılayabilir ve kopyalama ve yapıştırmaya özel kodla işleyebilirsiniz.

Verilerinizi düzenlemek için zaten bir içerik sağlayıcı, dahili veritabanı veya dahili tablo kullanıyorsanız normalde kodlama tekniğini kullanırsınız. Bu tür durumlarda, kopyalamak istediğiniz birden fazla veri parçası ve muhtemelen her parça için benzersiz bir tanımlayıcınız vardır. Yapıştırma uygulamasından gelen bir sorguya yanıt olarak, verileri tanımlayıcısına göre arayabilir ve döndürebilirsiniz.

Birden fazla veriniz yoksa büyük olasılıkla bir tanımlayıcı kodlamanız gerekmez. Sağlayıcınıza özgü bir URI kullanabilirsiniz. Sağlayıcınız, bir sorguya yanıt olarak şu anda içerdiği verileri döndürür.

Veri yapılarını kopyalama

Karmaşık verileri kopyalayıp yapıştırmak için bir içerik sağlayıcıyı bileşenin alt sınıfı olarak ayarlayın.ContentProvider Panoya koyduğunuz URI'yi, sağlamak istediğiniz tam kaydı işaret edecek şekilde kodlayın. Ayrıca, uygulamanızın mevcut durumunu da göz önünde bulundurun:

  • Halihazırda bir içerik sağlayıcınız varsa işlevini genişletebilirsiniz. Yalnızca veri yapıştırmak isteyen uygulamalardan gelen URI'leri işlemek için query() yöntemini değiştirmeniz gerekebilir. "Kopya" URI kalıbını işleyen yöntemi değiştirmeniz muhtemeldir.
  • Uygulamanızda dahili bir veritabanı varsa bu veritabanından kopyalama işlemini kolaylaştırmak için veritabanını bir içerik sağlayıcıya taşımak isteyebilirsiniz.
  • Veritabanı kullanmıyorsanız tek amacı, panosundan yapıştırma yapan uygulamalara veri sunmak olan basit bir içerik sağlayıcı uygulayabilirsiniz.

İçerik sağlayıcıda en az aşağıdaki yöntemleri geçersiz kılın:

query()
Yapıştırma uygulamaları, bu yöntemi kullanarak verileri, panoya eklediğiniz URI ile alabileceğini varsayar. Kopyalamayı desteklemek için bu yöntemin, özel bir "kopya" yolu içeren URI'leri algılamasını sağlayın. Ardından uygulamanız, kopya yolunu ve kopyalamak istediğiniz kaydın tam işaretçisini içeren, panosuna yerleştirmek için bir "kopya" URI'si oluşturabilir.
getType()
Bu yöntem, kopyalamak istediğiniz verilerin MIME türlerini döndürmelidir. newUri() yöntemi, MIME türlerini yeni ClipData nesnesine yerleştirmek için getType()'u çağırır.

Karmaşık veriler için MIME türleri İçerik sağlayıcılar bölümünde açıklanmıştır.

insert() veya update() gibi diğer içerik sağlayıcı yöntemlerinden herhangi birine sahip olmanız gerekmez. Yapıştırma uygulamasının tek yapması gereken, desteklenen MIME türlerinizi almak ve sağlayıcınızdan veri kopyalamaktır. Bu yöntemlere zaten sahipseniz kopyalama işlemlerini etkilemezler.

Aşağıdaki snippet'lerde, uygulamanızın karmaşık verileri kopyalayacak şekilde nasıl ayarlanacağı gösterilmektedir:

  1. Uygulamanızın genel sabitlerinde, verileri kopyalamak için kullandığınız URI dizelerini tanımlayan bir temel URI dizesi ve bir yol beyan edin. Kopyalanan veriler için bir MIME türü de belirtin.

    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";
  2. Kullanıcıların veri kopyaladığı etkinlikte, verileri panoya kopyalayacak şekilde kodu ayarlayın. Kopyalama isteğine yanıt olarak URI'yi panoya koyun.

    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);
  3. İçerik sağlayıcınızın global kapsamında bir URI eşleştirici oluşturun ve panoya yerleştirdiğiniz URI'lerle eşleşen bir URI kalıbı ekleyin.

    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);
  4. query() yöntemini ayarlayın. Bu yöntem, nasıl kodladığınıza bağlı olarak farklı URI kalıplarını işleyebilir ancak yalnızca panoya kopyalama işleminin kalıbı gösterilir.

    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.
        ...
    }
  5. Kopyalanan veriler için uygun bir MIME türü döndürmek üzere getType() yöntemini ayarlayın:

    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);
        ...
        }
    }

Verileri bir içerik URI'sinden yapıştırma bölümünde, içerik URI'sinin nasıl panoya alınacağı ve verileri almak ve yapıştırmak için nasıl kullanılacağı açıklanmaktadır.

Veri akışlarını kopyalama

Büyük miktarlarda metin ve ikili veriyi akış olarak kopyalayıp yapıştırabilirsiniz. Veriler aşağıdaki gibi biçimlere sahip olabilir:

  • Gerçek cihazda depolanan dosyalar
  • Yuvalardan akışlar
  • Sağlayıcının temel veritabanı sisteminde depolanan büyük miktarda veri

Veri akışları için içerik sağlayıcı, Cursor nesnesi yerine AssetFileDescriptor gibi bir dosya tanımlayıcısı nesnesi ile verilerine erişim sağlar. Yapıştırma uygulaması, bu dosya tanımlayıcısını kullanarak veri akışını okur.

Uygulamanızı, bir sağlayıcıyla veri akışı kopyalayacak şekilde ayarlamak için aşağıdaki adımları uygulayın:

  1. Panoya yerleştirdiğiniz veri akışı için bir içerik URI'si oluşturun. Bunu yapmak için şu seçenekleri kullanabilirsiniz:
    • URI'da tanımlayıcı kodlama bölümünde açıklandığı gibi veri akışı için bir tanımlayıcıyı URI'ye kodlayın ve ardından sağlayıcınızda tanımlayıcıları ve karşılık gelen akış adını içeren bir tablo tutun.
    • Akış adını doğrudan URI'de kodlayın.
    • Sağlayıcıdan her zaman mevcut yayını döndüren benzersiz bir URI kullanın. Bu seçeneği kullanıyorsanız yayını URI'yi kullanarak panoya kopyaladığınızda sağlayıcınızı farklı bir yayına yönlendirecek şekilde güncellemeyi unutmayın.
  2. Sunmayı planladığınız her veri akışı türü için bir MIME türü belirtin. Verileri yapıştıran uygulamalar, verileri panoya yapıştırıp yapıştıramayacaklarını belirlemek için bu bilgilere ihtiyaç duyar.
  3. Bir akış için dosya tanımlayıcı döndüren ContentProvider yöntemlerinden birini uygulayın. İçerik URI'sinde tanımlayıcıları kodlarsanız hangi akışın açılacağını belirlemek için bu yöntemi kullanın.
  4. Veri akışını panoya kopyalamak için içerik URI'sini oluşturup panoya yerleştirin.

Bir uygulama, veri akışını yapıştırmak için klibi panosundan alır, URI'yi alır ve akışı açan bir ContentResolver dosya tanımlayıcı yöntemi çağrısında kullanır. ContentResolver yöntemi, karşılık gelen ContentProvider yöntemini çağırarak içerik URI'sini iletir. Sağlayıcınız, dosya tanımlayıcısını ContentResolver yöntemine döndürür. Daha sonra, yapıştırma uygulaması akıştaki verileri okumakla yükümlüdür.

Aşağıdaki liste, bir içerik sağlayıcı için en önemli dosya tanımlayıcı yöntemlerini göstermektedir. Bunların her biri, yöntem adına "Tanımlayıcı" dizesi eklenmiş bir ContentResolver yöntemine sahiptir. Örneğin, openAssetFile() için ContentResolver analogu openAssetFileDescriptor()'dir.

openTypedAssetFile()

Bu yöntem, yalnızca sağlanan MIME türü sağlayıcı tarafından destekleniyorsa bir öğe dosyası tanımlayıcısı döndürür. Arayan (yapıştırma işlemini yapan uygulama) bir MIME türü kalıbı sağlar. Bir URI'yi panosuna kopyalayan uygulamanın içerik sağlayıcısı, MIME türünü sağlayabiliyorsa bir AssetFileDescriptor dosya tutamacını döndürür, sağlayamıyorsa istisna atar.

Bu yöntem, dosyaların alt bölümlerini işler. İçerik sağlayıcının panoya kopyaladığı öğeleri okumak için bu özelliği kullanabilirsiniz.

openAssetFile()
Bu yöntem, openTypedAssetFile() ürününün daha genel bir biçimidir. İzin verilen MIME türlerine göre filtreleme yapmaz ancak dosyaların alt bölümlerini okuyabilir.
openFile()
Bu, openAssetFile()'un daha genel bir biçimidir. Dosyaların alt bölümlerini okuyamaz.

Dilerseniz openPipeHelper() yöntemini dosya tanımlayıcı yönteminizle birlikte kullanabilirsiniz. Bu sayede yapıştırma uygulaması, bir boru kullanarak arka plandaki iş parçacığında akış verilerini okuyabilir. Bu yöntemi kullanmak için ContentProvider.PipeDataWriter arayüzünü uygulayın.

Etkili kopyalama ve yapıştırma işlevi tasarlayın

Uygulamanız için etkili bir kopyala ve yapıştır işlevi tasarlamak istiyorsanız aşağıdaki noktaları göz önünde bulundurun:

  • Herhangi bir zamanda, panosuzda yalnızca bir klip bulunur. Sistemdeki herhangi bir uygulama tarafından yapılan yeni bir kopyalama işlemi, önceki klibin üzerine yazılır. Kullanıcı, uygulamanızdan uzaklaşıp kopyalama işlemini yapmadan önce geri dönebileceğinden, panosunun kullanıcının daha önce uygulamanızda kopyaladığı klibi içerdiğini varsayamazsınız.
  • Klip başına birden fazla ClipData.Item nesnesi eklemenin amacı, tek bir seçime referans verme Genellikle bir klipteki tüm ClipData.Item öğelerin aynı şekle sahip olmasını istersiniz. Yani bunların tümü basit metin, içerik URI'si veya Intent olmalıdır ve bunların bir karışımı olmamalıdır.
  • Veri sağlarken farklı MIME temsilleri sunabilirsiniz. Desteklediğiniz MIME türlerini ClipDescription dosyasına ekleyin ve ardından MIME türlerini içerik sağlayıcınıza uygulayın.
  • Panodan veri aldığınızda, mevcut MIME türlerini kontrol etmekten ve varsa hangisinin kullanılacağına karar vermekten uygulamanız sorumludur. Panoda bir klip olsa ve kullanıcı bir yapıştırma isteğinde bulunsa bile uygulamanızın bu işlemi yapması gerekmez. MIME türü uyumluysa yapıştırma işlemini yapın. coerceToText() kullanarak panodaki verileri metne dönüştürebilirsiniz. Uygulamanız mevcut MIME türlerinden birden fazlasını destekliyorsa kullanıcının hangisini kullanacağını seçmesine izin verebilirsiniz.