النسخ واللصق

تجربة طريقة "الكتابة"
‫Jetpack Compose هي مجموعة أدوات واجهة المستخدم التي يُنصح باستخدامها على Android. تعرَّف على كيفية استخدام ميزة "النسخ واللصق" في Compose.

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

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

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

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

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

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

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

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

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

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

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

فئات الحافظة

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

ClipboardManager

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

‫ClipData وClipData.Item وClipDescription

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

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

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

نص
A CharSequence.
URI
A Uri. يحتوي هذا الحقل عادةً على معرّف موارد منتظم (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. إذا كان معرّف الموارد المنتظم (URI) هو معرّف موارد منتظم خاص بالمحتوى، أي إذا كانت الدالة Uri.getScheme() تعرض القيمة content:، تستخدم الطريقة الكائن ContentResolver المقدَّم في resolver لاسترداد أنواع MIME المتاحة من موفّر المحتوى. بعد ذلك، يتم تخزينها في ClipDescription. بالنسبة إلى معرّف موارد منتظم (URI) ليس معرّف موارد منتظم (URI) content:، تضبط الطريقة نوع MIME على MIMETYPE_TEXT_URILIST.

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

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() استخدامه كمعرّف موارد موحّد للمحتوى.
  • إذا كان معرّف الموارد المنتظم (URI) هو معرّف موارد منتظم خاص بالمحتوى وكان بإمكان مقدّم الخدمة عرض بث نصي، فإنّ coerceToText() يعرض بثًا نصيًا.
  • إذا كان معرّف الموارد المنتظم (URI) هو معرّف موارد منتظم خاص بالمحتوى ولكن لم يوفّر المزوّد بثًا نصيًا، ستعرض الدالة coerceToText() تمثيلاً لمعرّف الموارد المنتظم. ويكون التمثيل هو نفسه الذي تعرضه الدالة Uri.toString().
  • إذا لم يكن معرّف الموارد المنتظم (URI) معرّفًا لموارد المحتوى، تعرض الدالة coerceToText() تمثيلاً لمعرّف الموارد المنتظم (URI). ويكون التمثيل هو نفسه الذي تعرضه الدالة Uri.toString().
النية
إذا كان ClipData.Item عبارة عن Intent، أي إذا لم يكن getIntent() قيمة فارغة، تحوّله الدالة coerceToText() إلى معرّف موارد منتظم (URI) خاص بـ Intent وتعرضه. ويكون التمثيل هو نفسه الذي تعرضه الدالة 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. إذا كنت تنسخ البيانات باستخدام معرّف موارد منتظم (URI) للمحتوى، عليك إعداد موفّر محتوى.
  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).

      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. واجهة المستخدم التي تظهر عند إضافة محتوى إلى الحافظة في نظام التشغيل Android 13 والإصدارات الأحدث

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

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

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

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

عرض شريط المعلومات بعد إجراء عملية نسخ داخل التطبيق
الشكل 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(). تؤدي إضافة هذه العلامة إلى منع ظهور المحتوى الحسّاس في التأكيد المرئي للمحتوى المنسوخ في نظام التشغيل Android 13 والإصدارات الأحدث.

معاينة النص المنسوخ بدون الإبلاغ عن المحتوى الحسّاس
الشكل 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)
    }
}

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

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

لصق نص عادي

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

يوضّح الإجراء التالي كيفية الحصول على بيانات من موفّر محتوى استنادًا إلى معرّف الموارد المنتظم (URI) الخاص بالمحتوى في الحافظة. يتحقّق مما إذا كان نوع 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();
                 }
             }
         }
    }

لصق Intent

لصق غرض، عليك أولاً الحصول على الحافظة العامة. افحص العنصر 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 وتقديم بيانات معقّدة وتوفير عمليات بث للملفات. تفترض الأوصاف أنّك على دراية بالمبادئ العامة لتصميم موفّر المحتوى.

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

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

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

"content://com.example.contacts"

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

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) مخصّص للنسخ. على سبيل المثال، لنفترض أنّ لديك مسارات URI التالية:

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

يمكنك إضافة مسار آخر لنسخ معرّفات الموارد الموحّدة (URI) باتّباع الخطوات التالية:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

openTypedAssetFile()

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

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

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

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

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

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

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