النسخ واللصق

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

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

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

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

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

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

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

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

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

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

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

صفوف الحافظة

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

مدير الحافظة

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

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

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

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

إذا كان الكائن ClipData.Item يحتوي على معرّف موارد منتظم (URI) للمحتوى وقرّرت أنّه يمكنك معالجة أحد أنواع 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. اختبِر ما إذا كان معرّف الموارد المنتظم (URI) هو معرّف موارد منتظم (URI) للمحتوى من خلال طلب الرقم 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 للمحتوى الخاص ببياناتك، فهو بحاجة إلى معرفة جزء البيانات المراد استرداده. يمكنك توفير هذه المعلومات من خلال ترميز معرّف للبيانات الموجودة على عنوان URI نفسه، أو يمكنك توفير معرّف URI فريد يعرض البيانات التي تريد نسخها. يعتمد الأسلوب الذي تختاره على تنظيم بياناتك.

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

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

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

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

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

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

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

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

في موفِّر المحتوى، يمكنك تجاهل الطرق التالية على الأقل:

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

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

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

يمكنك نسخ ولصق كميات كبيرة من النصوص والبيانات الثنائية كمصادر بيانات. يمكن أن تحتوي البيانات على نماذج مثل ما يلي:

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

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

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

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

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

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

openTypedAssetFile()

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

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

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

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

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

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

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