النسخ واللصق

يوفر Android إطار عمل فعّالاً ومستندًا إلى الحافظة للنسخ واللصق. وهو يتيح أنواع البيانات البسيطة والمعقدة، بما في ذلك سلاسل النصوص وبنى البيانات المعقدة وبيانات تدفق النصوص والثنائيات ومواد عرض التطبيقات. يتم تخزين بيانات النصوص البسيطة مباشرةً في الحافظة، في حين يتم تخزين البيانات المعقدة كمرجع يحلّه تطبيق اللصق مع مقدّم المحتوى. تعمل ميزة النسخ واللصق داخل التطبيق وبين التطبيقات التي تنفِّذ الإطار العملي.

بما أنّ جزءًا من إطار العمل يستخدم مقدّمي المحتوى، يفترض هذا المستند بعض الإلمام بواجهة برمجة التطبيقات Android Content Provider API، الموضّحة في مقدّمو المحتوى.

يتوقع المستخدمون تلقّي ملاحظات عند نسخ المحتوى إلى الحافظة، لذا بالإضافة إلى إطار العمل الذي يُمكّن من النسخ واللصق، يعرض Android واجهة مستخدم تلقائية للمستخدمين عند النسخ في الإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) والإصدارات الأحدث. وبسبب هذه الميزة، هناك خطر تكرار إشعار. يمكنك معرفة المزيد من المعلومات حول هذه الحالة الهامشية في قسم تجنب الإشعارات المكررة.

صورة متحركة تعرض إشعار حافظة Android 13
الشكل 1. واجهة المستخدم التي تظهر عند نقل المحتوى إلى الحافظة في Android 13 والإصدارات الأحدث

تقديم ملاحظات يدويًا إلى المستخدمين عند نسخ البيانات في Android 12L (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأقدم اطّلِع على الاقتراحات المتعلّقة بهذا في هذا المستند.

إطار عمل الحافظة

عند استخدام إطار عمل الحافظة، ضَع البيانات في عنصر مقطع، ثم ضَع عنصر المقطع في الحافظة على مستوى النظام. يمكن أن يتّخذ عنصر المقطع أحد الأشكال الثلاثة التالية:

نص
سلسلة نصية. ضع السلسلة مباشرةً في عنصر المقطع، ثم ضعه في حافظة النصوص. للصق السلسلة، احصل على عنصر المقطع من الحافظة وانسخ السلسلة إلى مساحة تخزين تطبيقك.
URI
كائن Uri يمثّل أي شكل من أشكال معرّف الموارد المنتظم (URI). الهدف الأساسي هو نسخ البيانات المعقدة من موفّر المحتوى. لنسخ البيانات، ضَع كائن Uri في عنصر المقطع ثم ضَع عنصر المقطع على الحافظة. ولصق البيانات، احصل على عنصر المقطع، واحصل على عنصر Uri، وحوِّله إلى مصدر بيانات، مثل مزوّد محتوى، وانسخ البيانات من المصدر إلى مساحة تخزين تطبيقك.
النيّة بالشراء
Intent. ويسمح هذا الإجراء بنسخ اختصارات التطبيقات. لنسخ البيانات، أنشئ رمز Intent، ثم ضعه في عنصر مقطع، ثم ضع عنصر المقطع في الحافظة. ولصق البيانات، احصل على عنصر المقطع، ثم انسخ عنصر Intent إلى منطقة ذاكرة تطبيقك.

لا تحتوي الحافظة على عنصر مقطع واحد فقط في المرة الواحدة. فعندما يضع تطبيق ما كائنًا على لوحة المفاتيح، يختفي كائن المقطع السابق.

إذا كنت تريد السماح للمستخدمين بلصق البيانات في تطبيقك، ليس عليك معالجة جميع أنواع البيانات. يمكنك فحص البيانات في الحافظة قبل منح المستخدمين خيار لصقها. بالإضافة إلى توفُّر نموذج بيانات معيّن، يحتوي كائن المقطع أيضًا على بيانات وصفية تخبرك بأنواع MIME المتاحة. تساعدك البيانات الوصفية هذه على تحديد ما إذا كان يمكن لتطبيقك القيام بشيء مفيد باستخدام بيانات الحافظة. على سبيل المثال، إذا كان لديك تطبيق يعالج النصوص في المقام الأول، قد تريد تجاهل عناصر المقاطع التي تحتوي على معرّف موارد منتظم أو نية.

يمكنك أيضًا السماح للمستخدمين بلصق النص بغض النظر عن شكل البيانات في الحافظة. للقيام بذلك، اجبر بيانات الحافظة على تمثيل نصي، ثم الصق هذا النص. وسيتم وصف ذلك في قسم إجبار الحافظة على نص.

فئات الحافظة

يصف هذا القسم الفئات التي يستخدمها إطار عمل الحافظة.

مدير الحافظة

يتم تمثيل حافظة نظام Android من خلال فئة ClipboardManager الشاملة. لا تنشئ مثيلًا لهذه الفئة مباشرةً. بدلاً من ذلك، يمكنك الإشارة إليها من خلال استدعاء getSystemService(CLIPBOARD_SERVICE).

ClipData وClipData.Item وClipDescription

لإضافة بيانات إلى الحافظة، أنشئ عنصر ClipData يحتوي على وصف للبيانات والبيانات نفسها. يمكن للحافظة الاحتفاظ بClipData واحد فقط في كل وقت. يحتوي ClipData على كائن ClipDescription وكائن واحد أو أكثر ClipData.Item.

يحتوي عنصر ClipDescription على بيانات وصفية عن المقطع. على وجه الخصوص، يحتوي على صفيف من أنواع MIME المتاحة لبيانات المقطع. بالإضافة إلى ذلك، في Android 12 (المستوى 31 لواجهة برمجة التطبيقات) والإصدارات الأحدث، تتضمن البيانات الوصفية معلومات حول ما إذا كان العنصر يحتوي على نص بنمط معيّن ونوع النص في العنصر. عند وضع مقطع في الحافظة، تتوفر هذه المعلومات لتطبيقات اللصق، التي يمكنها التحقق مما إذا كان يمكنها معالجة بيانات المقطع أم لا.

يحتوي عنصر ClipData.Item على النص أو معرّف الموارد المنتظم (URI) أو بيانات النية:

نص
أ CharSequence.
URI
أ Uri. يحتوي هذا العنصر عادةً على معرّف موارد منتظم لموفّر المحتوى، مع أنّه يُسمح باستخدام أي معرّف موارد منتظم. يضع التطبيق الذي يقدّم البيانات معرّف الموارد المنتظم (URI) في الحافظة. تحصل التطبيقات التي تريد لصق البيانات على معرّف الموارد المنتظم من الحافظة وتستخدمه للوصول إلى موفّر المحتوى أو مصدر بيانات آخر واسترداد البيانات.
النيّة بالشراء
أحد Intent. يتيح لك نوع البيانات هذا نسخ اختصار تطبيق إلى الحافظة. ويمكن للمستخدمين بعد ذلك لصق الاختصار في تطبيقاتهم لاستخدامه لاحقًا.

يمكنك إضافة أكثر من عنصر ClipData.Item واحد إلى المقطع. يتيح ذلك للمستخدمين نسخ عدة مقاطع و لصقها كلّها في مقطع واحد. على سبيل المثال، إذا كانت لديك أداة مصغّرة لقائمة تتيح للمستخدِم اختيار أكثر من عنصر واحد في كل مرة، يمكنك نسخ جميع العناصر إلى الحافظة في آنٍ واحد. لإجراء ذلك، أنشئ عنصر ClipData.Item منفصلاً لكل عنصر قائمة، ثم أضِف عناصر ClipData.Item إلى عنصر ClipData.

طرق تسهيل استخدام ClipData

توفّر الفئة ClipData طرق ملائمة ثابتة لإنشاء كائن ClipData باستخدام كائن ClipData.Item واحد وكائن ClipDescription بسيط:

newPlainText(label, text)
يعرض عنصر ClipData يحتوي على عنصر ClipData.Item واحد يتضمّن سلسلة نصية. تم ضبط تصنيف الكائن ClipDescription على label. نوع MIME الوحيد في ClipDescription هو MIMETYPE_TEXT_PLAIN.

استخدِم newPlainText() لإنشاء مقطع من سلسلة نصية.

newUri(resolver, label, URI)
عرض كائن ClipData الذي يحتوي كائن ClipData.Item الفردي الذي يحتوي على معرّف موارد منتظم (URI). تم ضبط تصنيف الكائن ClipDescription على label. إذا كان عنوان URL هو عنوان URL للمحتوى، أي إذا كان Uri.getScheme() يعرضcontent:، تستخدِم الطريقة العنصر ContentResolver المقدَّم فيresolver لاسترداد أنواع MIME المتاحة من مقدّم المحتوى. وبعد ذلك، يتم تخزينها في ClipDescription. بالنسبة إلى معرّف الموارد المنتظم (URI) الذي ليس معرّف موارد content:، تضبط الطريقة نوع MIME على MIMETYPE_TEXT_URILIST.

استخدِم newUri() لإنشاء مقطع من عنوان URL، خاصةً عنوان content: URL.

newIntent(label, intent)
يعرض عنصر ClipData يحتوي على عنصر ClipData.Item واحد يحتوي على Intent. تم ضبط تصنيف العنصر ClipDescription على label. تم ضبط نوع MIME على MIMETYPE_TEXT_INTENT.

استخدِم newIntent() لإنشاء مقطع من عنصر Intent.

تحويل بيانات الحافظة إلى نص

حتى إذا كان تطبيقك لا يتعامل إلا مع النصوص، يمكنك نسخ البيانات غير النصية من الحافظة من خلال تحويلها باستخدام الأسلوب ClipData.Item.coerceToText().

تحوِّل هذه الطريقة البيانات في ClipData.Item إلى نص وتُعيد CharSequence. تستند القيمة التي تعرضها الدالة ClipData.Item.coerceToText() إلى شكل البيانات في ClipData.Item:

نص
إذا كانت ClipData.Item عبارة عن نص، أي إذا لم تكن getText() قيمة فارغة، تعرض coerceToText() النص.
URI
إذا كان ClipData.Item هو معرّف موارد منتظم، أي إذا كان getUri() غير صفري، يحاول coerceToText() استخدامه كمعرّف موارد منتظم للمحتوى.
  • إذا كان معرّف الموارد المنتظم هو معرّف موارد منتظم للمحتوى وكان بإمكان الموفّر عرض بث نصي، تعرِض coerceToText() بثًا نصيًا.
  • إذا كان معرّف الموارد المنتظم هو معرّف موارد منتظم للمحتوى ولكنّ مقدّم الخدمة لا يقدّم بثًا نصيًا، تعرِض coerceToText() تمثيلًا لمعرّف الموارد المنتظم. يكون التمثيل هو نفسه التمثيل الذي يعرضه Uri.toString().
  • إذا لم يكن عنوان URL هو عنوان URL للمحتوى، تعرض coerceToText() تمثيلاً ل عنوان URL. التمثيل هو نفسه التمثيل الذي يعرضه Uri.toString().
النيّة
إذا كان ClipData.Item Intent، أي إذا كان getIntent() غير صفري، تحوّله الدالة coerceToText() إلى معرّف موارد منتظم لطلب بحث وتُعرِضه. التمثيل هو نفسه التمثيل الذي يعرضه Intent.toUri(URI_INTENT_SCHEME).

تم تلخيص إطار عمل الحافظة في الشكل 2. لنسخ البيانات، يضع التطبيق كائن ClipData في حافظة ClipboardManager العامة. يحتوي العنصر ClipData على عنصر ClipData.Item واحد أو أكثر وعنصر ClipDescription واحد. ولصق البيانات، يحصل التطبيق على ClipData، ويحصل على نوع MIME من ClipDescription، ويحصل على البيانات من ClipData.Item أو من موفِّر المحتوى المُشار إليه في ClipData.Item.

صورة تعرِض مخطّطًا بصريًا لإطار عمل النسخ واللصق
الشكل 2. إطار عمل حافظة Android

نسخ إلى الحافظة

لنسخ البيانات إلى الحافظة، احصل على معرّف للعنصر ClipboardManager العام، وأنشئ عنصر ClipData، وأضِف عنصر ClipDescription وعنصرًا واحدًا أو أكثر ClipData.Item إليه. بعد ذلك، أضِف عنصر ClipData المكتمل إلى ClipboardManager. يتم وصف ذلك بالتفصيل في الإجراء التالي:

  1. إذا كنت تنسخ البيانات باستخدام معرّف موارد منتظم للمحتوى، عليك إعداد مقدّم محتوى.
  2. الحصول على الحافظة في النظام:

    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. انسخ البيانات إلى عنصر ClipData جديد:

    • للنص

      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

      ينشئ هذا المقتطف معرّف موارد منتظم (URI) من خلال ترميز رقم تعريف السجلّ على معرّف الموارد المنتظم (URI) الخاص بالمحتوى للموفِّر. يمكنك الاطّلاع على مزيد من التفاصيل حول هذه الطريقة في القسم ترميز معرّف في معرّف الموارد المنتظم (URI).

      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 لتطبيق معيّن، ثم يضع ذلك في عنصر المقطع:

      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. ضع عنصر المقطع الجديد في الحافظة:

    Kotlin

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

    Java

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

تقديم ملاحظات عند النسخ إلى الحافظة

يتوقع المستخدمون الحصول على ملاحظات مرئية عندما ينسخ تطبيق محتوى إلى الحافظة. يتم تنفيذ ذلك تلقائيًا للمستخدمين الذين يستخدمون الإصدار 13 من نظام التشغيل Android والإصدارات الأحدث، ولكن يجب تنفيذه يدويًا في الإصدارات السابقة.

بدءًا من Android 13، يعرض النظام تأكيدًا مرئيًا عاديًا عند إضافة محتوى إلى الحافظة. ينفّذ التأكيد الجديد ما يلي:

  • لتأكيد نسخ المحتوى بنجاح
  • يوفّر معاينة للمحتوى المنسوخ.

صورة متحرّكة تعرض إشعارًا في حافظة Android 13
الشكل 3. واجهة مستخدم تظهر عند إدخال المحتوى إلى الحافظة في الإصدار 13 من Android والإصدارات الأحدث

في الإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأقدم، قد لا يكون المستخدمون متأكدين مما إذا كانوا قد نسخوا المحتوى بنجاح أو ما نسخوه. توحِّد هذه الميزة الإشعارات المختلفة التي تعرضها التطبيقات بعد النسخ وتوفر للمستخدمين مزيدًا من التحكم في الحافظة.

تجنُّب الإشعارات المكرّرة

أمّا في Android 12L (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأقدم، فننصح بتنبيه المستخدمين عند إتمام النسخ بنجاح من خلال إرسال ملاحظات مرئية داخل التطبيق باستخدام أداة، مثل Toast أو Snackbar، بعد النسخ.

لتجنُّب عرض المعلومات بشكلٍ مكرّر، ننصحك بشدة بإزالة الإشعارات القصيرة أو أشرطة المعلومات الصغيرة التي تظهر بعد نسخة داخل التطبيق لنظام التشغيل Android 13 والإصدارات الأحدث.

انشر شريط المعلومات المصغّر بعد نسخة داخل التطبيق.
الشكل 4. إذا عرضت شريطًا صغيرًا لتأكيد النسخ في Android 13، ستظهر للمستخدم رسائل مكرّرة.
نشر إشعار منبثق بعد إنشاء نسخة داخل التطبيق
الشكل 5. إذا عرضت إشعارًا مؤقتًا لتأكيد النسخ في Android 13، سيرى المستخدم رسائل مكرّرة.

في ما يلي مثال على كيفية تنفيذ ذلك:

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

إضافة محتوى حسّاس إلى الحافظة

إذا كان تطبيقك يسمح للمستخدمين بنسخ المحتوى الحسّاس إلى الحافظة، مثل كلمات المرور أو بيانات بطاقة الائتمان، يجب إضافة علامة إلى ClipDescription في ClipData قبل طلب الرقم ClipboardManager.setPrimaryClip(). تؤدي إضافة هذه العلامة إلى منع ظهور المحتوى الحسّاس في التأكيد المرئي للمحتوى المنسوخ في الإصدار 13 من نظام التشغيل Android والإصدارات الأحدث.

معاينة النص المنسوخ بدون الإبلاغ عن محتوى حسّاس
الشكل 6. معاينة النص المنسوخ بدون وضع علامة على المحتوى الحسّاس
رصد محتوى حسّاس في معاينة النص المنسوخ
الشكل 7. معاينة النص المنسوخ مع وضع علامة على المحتوى الحسّاس

للإبلاغ عن محتوى حسّاس، أضِف قيمة منطقية إضافية إلى 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)
    }
}

اللصق من الحافظة

كما هو موضّح سابقًا، يمكنك لصق البيانات من الحافظة من خلال الحصول على عنصر الحافظة العام، والحصول على عنصر المقطع، والاطّلاع على بياناته، ونسخ البيانات من عنصر المقطع إلى مساحة التخزين الخاصة بك إن أمكن. يوضّح هذا القسم بالتفصيل كيفية لصق الأشكال الثلاثة لبيانات clipboard.

لصق نص عادي

للصق نص عادي، يمكنك الحصول على الحافظة العامة والتأكّد من إمكانية عرضها لنص عادي. بعد ذلك، احصل على عنصر المقطع وانسخ نصه إلى مساحة التخزين الخاصة بك باستخدام getText()، كما هو موضّح في الإجراء التالي:

  1. احصل على الكائن الشامل ClipboardManager باستخدام getSystemService(CLIPBOARD_SERVICE). يمكنك أيضًا تحديد متغيّر عام يحتوي على النص الذي تمّ لصقه:

    Kotlin

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

    Java

    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    String pasteData = "";
  2. حدِّد ما إذا كنت بحاجة إلى تفعيل خيار "اللصق" أو إيقافه في النشاط الحالي. تأكَّد من أنّ الحافظة تحتوي على مقطع وأنّه يمكنك التعامل مع نوع البيانات التي يمثّلها المقطع:

    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. انسخ البيانات من الحافظة. لا يمكن الوصول إلى هذه النقطة في الرمز إلا إذا كان خيار القائمة "لصق" مفعّلاً، لذا يمكنك افتراض أنّ الحافظة تحتوي على نص عادي. لا تعرف بعد ما إذا كان يحتوي على سلسلة نصية أو معرّف موارد منتظم يشير إلى نص عادي. يختبر مقتطف الرمز التالي ذلك، ولكنه يعرض فقط الرمز المخصّص لمعالجة النص العادي:

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

لصق البيانات من معرّف موارد منتظم للمحتوى

إذا كان عنصر ClipData.Item يحتوي على رابط موارد موحّد للمحتوى وتبيّن لك أنّه يمكنك التعامل مع أحد أنواع MIME الخاصة به، أنشئ ContentResolver واطلب من طريقة مقدّم المحتوى المناسبة استرداد البيانات.

توضّح الخطوات التالية كيفية الحصول على بيانات من موفّر محتوى استنادًا إلى عنوان URL لمحتوى في الحافظة. يتحقّق من توفّر نوع MIME يمكن للتطبيق استخدامه من العميل.

  1. يمكنك تحديد متغيّر عام يحتوي على نوع 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";
  2. الحصول على الحافظة الشاملة احصل أيضًا على أداة حلّ لمشاكل المحتوى حتى تتمكّن من الوصول إلى مقدّم المحتوى:

    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. احصل على المقطع الأساسي من الحافظة واحصل على محتواه كمعرّف الموارد المنتظم (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();
  4. يمكنك اختبار ما إذا كان معرّف الموارد المنتظم هو معرّف موارد منتظم للمحتوى من خلال الاتصال بـ getType(Uri). تعرِض هذه الطريقة قيمة فارغة إذا لم تشير Uri إلى مقدّم محتوى صالح.

    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. اختبِر ما إذا كان مقدّم المحتوى يتيح استخدام نوع MIME يتوافق مع التطبيق. إذا حدث ذلك، يمكنك الاتصال بـ ContentResolver.query() للحصول على البيانات. قيمة الإرجاع هي 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();
                 }
             }
         }
    }

لصق نية

ولصق نية، عليك أولاً الحصول على الحافظة الشاملة. افحص كائن ClipData.Item لمعرفة ما إذا كان يحتوي على Intent. بعد ذلك، يمكنك استدعاء getIntent() لنسخ النية إلى مساحة التخزين لديك. يوضّح المقتطف التالي ذلك:

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

إشعار النظام الذي يظهر عندما يصل تطبيقك إلى بيانات الحافظة

في نظام التشغيل Android 12 (المستوى 31 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يعرض النظام عادةً رسالة إشعار منبثق عند استدعاء تطبيقك getPrimaryClip(). يتضمّن النص داخل الرسالة التنسيق التالي:

APP pasted from your clipboard

لا يعرض النظام رسالة فورية عندما ينفّذ تطبيقك أحد الإجراءات التالية:

  • الوصول إلى ClipData من تطبيقك
  • الوصول بشكل متكرّر إلى ClipData من تطبيق معيّن. لا يظهر إشعار الشاشة المنبثقة إلا عند وصول تطبيقك إلى البيانات من هذا التطبيق للمرة الأولى.
  • يسترجع البيانات الوصفية لكائن المقطع، مثلاً من خلال استدعاء getPrimaryClipDescription() بدلاً من getPrimaryClip().

استخدام موفّري المحتوى لنسخ البيانات المعقدة

يسمح مقدّمو المحتوى بنسخ البيانات المعقدة، مثل سجلّات قاعدة البيانات أو مصادر ملفات. لنسخ البيانات، ضَع معرّف موارد منتظم للمحتوى في الحافظة. تحصل تطبيقات اللصق بعد ذلك على معرّف الموارد المنتظم هذا من ملف الحافظة وتستخدمه لاسترداد بيانات قاعدة البيانات أو أوصاف تدفق الملفات.

بما أنّ تطبيق اللصق لا يحتوي إلا على معرّف الموارد المنتظم للمحتوى الخاص ببياناتك، يجب أن يعرف قطعة البيانات التي يجب استرجاعها. يمكنك تقديم هذه المعلومات من خلال ترميز معرّف للبيانات في معرّف الموارد المنتظم نفسه، أو يمكنك تقديم معرّف موارد منتظم فريد يعرض البيانات التي تريد نسخها. يعتمد الأسلوب الذي تختاره على تنظيم بياناتك.

توضّح الأقسام التالية كيفية إعداد معرّفات URI وتقديم بيانات معقّدة وتقديم ملف البث. تفترض الأوصاف أنّك على دراية بالمبادئ العامة لتصميم موفّري المحتوى.

ترميز معرّف في معرّف الموارد المنتظم (URI)

هناك أسلوب مفيد لنسخ البيانات إلى الحافظة باستخدام عنوان URI وهو ترميز المعرّف للبيانات على معرّف الموارد المنتظم نفسه. يمكن لموفّر المحتوى بعد ذلك الحصول على المعرّف من عنوان URL واستخدامه لاسترداد البيانات. ولا يلزم أن يعرف تطبيق اللصق بوجود المعرّف. بل يجب عليه فقط الحصول على "المرجع"، وهو عنوان URI بالإضافة إلى المعرّف، من الحافظة، وتزويده بموفِّر المحتوى، واسترجاع البيانات.

يتم عادةً ترميز معرّف في معرّف موارد منتظم للمحتوى من خلال إلحاقه بنهاية معرّف الموارد المنتظم. على سبيل المثال، لنفترض أنّك حدّدت معرّف الموارد المنتظم لمقدّم الخدمة على النحو التالي:

"content://com.example.contacts"

إذا كنت تريد ترميز اسم في معرّف الموارد المنتظم هذا، استخدِم مقتطف الرمز التالي:

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

إذا كنت تستخدم موفّر محتوى، قد تحتاج إلى إضافة مسار URI جديد يشير إلى أنّ عنوان URI مخصّص للنسخ. على سبيل المثال، لنفترض أنّ لديك مسارات عناوين URL التالية:

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

يمكنك إضافة مسار آخر لنسخ عناوين URL:

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

يمكنك بعد ذلك رصد عنوان URL "للنسخ" من خلال مطابقة الأنماط ومعالجته باستخدام رمز مخصّص للنسخ واللصق.

ويمكنك استخدام أسلوب الترميز عادةً إذا كنت تستخدم حاليًا مزوّد محتوى أو قاعدة بيانات داخلية أو جدولًا داخليًا لتنظيم بياناتك. في هذه الحالات، يكون لديك عدة أجزاء من البيانات التي تريد نسخها، ومن المفترض أن يكون لكل جزء معرّف فريد. استجابةً لاستعلام من تطبيق لصق البيانات، يمكنك البحث عن البيانات حسب معرّفها وإعادتها.

إذا لم يكن لديك عدة أجزاء من البيانات، قد لا تحتاج إلى ترميز معرّف. يمكنك استخدام معرّف موارد فريد لموفّر الخدمة. ردًا على طلب بحث، يعرض مقدّم الخدمة البيانات التي يتضمّنها حاليًا.

نسخ هياكل البيانات

إعداد موفِّر محتوى لنسخ البيانات المعقدة ولصقها كصنف فرعي من العنصر ContentProvider شفِّر معرف الموارد المنتظم (URI) الذي تضعه في الحافظة بحيث يشير إلى السجل الدقيق الذي تريد توفيره. بالإضافة إلى ذلك، ننصحك بالاطّلاع على الحالة الحالية لتطبيقك:

  • إذا كان لديك مقدّم محتوى، يمكنك إضافة وظائف إليه. قد تحتاج فقط إلى تعديل طريقة query() لمعالجة عناوين URL الواردة من التطبيقات التي تريد لصق البيانات. من المحتمل أن تريد تعديل الطريقة لمعالجة نمط عنوان URL "copy".
  • إذا كان تطبيقك يحتفظ بقاعدة بيانات داخلية، قد تحتاج إلى نقل قاعدة البيانات هذه إلى مقدّم محتوى لتسهيل النسخ منها.
  • إذا لم تكن تستخدم قاعدة بيانات، يمكنك تنفيذ موفِّر محتوى بسيط الغرض الوحيد منه هو تقديم البيانات للتطبيقات التي تنسخها من الحافظة.

في موفّر المحتوى، يمكنك إلغاء الطرق التالية على الأقل:

query()
تفترض التطبيقات التي يتم لصقها أنّ بإمكانها الحصول على بياناتك باستخدام هذه الطريقة مع معرّف الموارد المنتظم (URI) الذي تضعه في الحافظة. لإتاحة النسخ، يجب أن ترصد هذه الطريقة عناوين URL التي تحتوي على مسار "copy" خاص. يمكن لتطبيقك بعد ذلك إنشاء عنوان URL لـ "النسخ" لوضعه في ملف الحافظة الذي يحتوي على مسار النسخ ومُشير إلى السجلّ الدقيق الذي تريد نسخه.
getType()
يجب أن تعرِض هذه الطريقة أنواع MIME للبيانات التي تريد نسخها. تستدعي الطريقة newUri() السمة getType() لوضع أنواع MIME في كائن ClipData الجديد.

يتم وصف أنواع MIME للبيانات المعقدة في موفّرو المحتوى.

لست بحاجة إلى استخدام أي من طرق مقدّمي المحتوى الأخرى، مثل insert() أو update(). ما على تطبيق اللصق سوى الحصول على أنواع بروتوكول MIME المتوافقة ونسخ البيانات من مقدّم الخدمة. إذا كانت هذه الطرق متوفّرة لديك، لن تتداخل مع عمليات النسخ.

توضِّح المقتطفات التالية كيفية إعداد تطبيقك لنسخ البيانات المعقدة:

  1. في الثوابت العالمية لتطبيقك، أدخِل سلسلة معرّف موارد منتظم (URI) أساسي ومسارًا يحدد سلاسل معرّفات الموارد المنتظمة التي تستخدمها لنسخ البيانات. يجب أيضًا تحديد نوع MIME للبيانات التي تم نسخها.

    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. في النشاط الذي ينسخ المستخدمون البيانات منه، يمكنك إعداد الرمز البرمجي لنسخ البيانات إلى الحافظة. استجابةً لطلب النسخ، ضَع معرّف الموارد المنتظم (URI) في الحافظة.

    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. في النطاق العالمي لموفّر المحتوى، أنشئ أداة مطابقة لمعرّفات الموارد المنتظمة (URI) وأضِف نمطًا لمعرّف الموارد المنتظم (URI) يتطابق مع معرّفات الموارد المنتظمة (URI) التي تضعها في الحافظة.

    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(). يمكن لهذه الطريقة التعامل مع أنماط عناوين URL مختلفة، استنادًا إلى كيفية ترميزها، ولكن لا يظهر سوى نمط عملية النسخ إلى الحافظة.

    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. يمكنك إعداد طريقة getType() لعرض نوع MIME مناسب للبيانات التي تم نسخها:

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

يصف قسم لصق البيانات من معرّف الموارد المنتظم (URI) للمحتوى كيفية الحصول على معرّف موارد منتظم (URI) للمحتوى من الحافظة واستخدامه للحصول على البيانات ولصقها.

نسخ مصادر البيانات

يمكنك نسخ ولصق كميات كبيرة من النصوص والبيانات الثنائية كبثّ. يمكن أن تتضمّن البيانات أشكالًا مثل ما يلي:

  • الملفات المخزَّنة على الجهاز الفعلي
  • مصادر البيانات من المقابس
  • كميات كبيرة من البيانات المخزنة في نظام قاعدة البيانات الأساسية للموفر

يقدّم مقدّم المحتوى لملفات بيانات البث إمكانية الوصول إلى بياناته باستخدام كائن وصف الملف، مثل AssetFileDescriptor، بدلاً من كائن Cursor. يقرأ تطبيق اللصق مصدر البيانات باستخدام واصف الملف هذا.

لإعداد تطبيقك لنسخ مصدر بيانات مع مزوّد، اتّبِع الخطوات التالية:

  1. يمكنك إعداد معرّف موارد منتظم (URI) للمحتوى لمصدر البيانات الذي تضعه في الحافظة. وتشمل خيارات إجراء ذلك ما يلي:
    • رمزِّ معرّفًا لمصدر البيانات في معرّف الموارد المنتظم (URI)، كما هو موضّح في القسم ترميز معرّف في معرّف الموارد المنتظم (URI)، ثم احتفظ بجدول في موفِّر البيانات يحتوي على المعرّفات واسم مصدر البيانات المقابل.
    • يجب ترميز اسم مصدر البيانات مباشرةً في معرّف الموارد المنتظم (URI).
    • استخدِم معرّف موارد منتظم (URI) فريد يعرِض دائمًا البث الحالي من الموفّر. في حال استخدام هذا الخيار، تذكَّر تعديل مقدّم الخدمة للإشارة إلى مصدر بيانات مختلف عند نسخ مصدر البيانات إلى الحافظة باستخدام معرّف الموارد المنتظم.
  2. أدخِل نوع MIME لكل نوع من مصادر البيانات التي تخطّط لتقديمها. تحتاج تطبيقات اللصق إلى هذه المعلومات لتحديد ما إذا كان بإمكانها لصق البيانات في الحافظة.
  3. نفِّذ إحدى طرق ContentProvider التي تُرجع واصف ملف لشدَّة البث. في حال ترميز المعرّفات في معرّف الموارد المنتظم للمحتوى، استخدِم هذه الطريقة لتحديد البث الذي تريد فتحه.
  4. لنسخ مصدر البيانات إلى الحافظة، عليك إنشاء معرّف الموارد المنتظم للمحتوى ووضعه في الحافظة.

ولصق مصدر بيانات، يحصل التطبيق على المقطع من الحافظة، ويحصل على معرّف الموارد المنتظم، ويستخدمه في طلب إلى طريقة وصف ملف ContentResolver التي تفتح مصدر البيانات. وتستدعي الطريقة ContentResolver طريقة ContentProvider المقابلة، مع تمريرها معرّف الموارد المنتظم (URI) للمحتوى. يعيد موفّر الخدمة واصف الملف إلى الطريقة ContentResolver. بعد ذلك، يتحمّل تطبيق اللصق مسؤولية قراءة البيانات من مصدر البيانات.

تعرض القائمة التالية أهم طرق وصف الملفات لموفّر المحتوى. ولكلٍّ من هذه الطرق طريقة ContentResolver مقابلة مع إلحاق السلسلة "Descriptor" باسم الطريقة. على سبيل المثال، ContentResolver المكافئ ل openAssetFile() هو openAssetFileDescriptor().

openTypedAssetFile()

تعرض هذه الطريقة واصفًا لملف مادة العرض، ولكن فقط إذا كان نوع MIME الذي تم تقديمه متوافقًا مع موفّر الخدمة. يقدّم المُرسِل، أي التطبيق الذي يُجري عملية اللصق، نمطًا لنوع MIME. يعرض مقدّم محتوى التطبيق الذي ينسخ معرّف موارد منتظم (URI) إلى ملف AssetFileDescriptor إذا كان بإمكانه تقديم نوع AssetFileDescriptor MIME، ويُظهر استثناءً إذا لم يكن بإمكانه ذلك.

تعالج هذه الطريقة الأقسام الفرعية من الملفات. ويمكنك استخدامها لقراءة مواد العرض التي نسخها موفّر المحتوى إلى الحافظة.

openAssetFile()
هذه الطريقة هي شكل أكثر عمومية من openTypedAssetFile(). ولا يفرِّط في قراءة أنواع MIME المسموح بها، ولكن يمكنه قراءة الأقسام الفرعية من الملفات.
openFile()
هذا شكل أكثر عمومية من openAssetFile(). لا يمكنه قراءة الأقسام الفرعية من الملفات.

يمكنك اختياريًا استخدام الطريقة openPipeHelper() مع طريقة وصف الملف. يتيح ذلك لتطبيق اللصق قراءة بيانات البث في سلسلسة مهام في الخلفية باستخدام أنبوب. لاستخدام هذه الطريقة، نفِّذ واجهة ContentProvider.PipeDataWriter.

تصميم وظيفة فعّالة للنسخ واللصق

لتصميم وظيفة فعالة للنسخ واللصق في تطبيقك، يجب مراعاة النقاط التالية:

  • هناك مقطع واحد فقط في الحافظة في أي وقت. يؤدي إجراء عملية نسخ جديدة من خلال أي تطبيق في النظام إلى استبدال المقطع السابق. بما أنّ المستخدم قد ينتقل بعيدًا عن تطبيقك وينسخ المحتوى قبل العودة، لا يمكنك افتراض أنّ الحافظة تحتوي على المقطع الذي نسخه المستخدم سابقًا في تطبيقك.
  • الغرض المقصود من استخدام عناصر ClipData.Item متعددة في كل مقطع هو السماح بنسخ ولصق اختيارات متعددة بدلاً من أشكال مختلفة لإشارة إلى اختيار واحد. يجب عادةً أن يكون لكل العناصر ClipData.Item في المقطع النموذج نفسه. وهذا يعني أنّه يجب أن تكون جميعها نصًا بسيطًا أو محتوى URI أو Intent، وألا تكون مختلطة.
  • عند تقديم البيانات، يمكنك تقديم تمثيلات MIME مختلفة. أضِف أنواع MIME المتوافقة مع تطبيقك إلى ClipDescription، ثم نفِّذ أنواع MIME في موفِّر المحتوى.
  • عند الحصول على بيانات من الحافظة، يكون تطبيقك مسؤولاً عن التحقّق من أنواع MIME المتاحة، ثم تحديد أي نوع، إن توفّر. حتى إذا كان هناك مقطع في الحافظة وطلب المستخدم لصقه، ليس مطلوبًا من تطبيقك لصقه. الصِق المحتوى إذا كان نوع MIME متوافقًا. يمكنك تحويل البيانات في الحافظة إلى نص باستخدام coerceToText(). إذا كان تطبيقك يتيح استخدام أكثر من نوع واحد من أنواع MIME المتاحة، يمكنك السماح للمستخدم باختيار النوع الذي يريده.