تلقّي محتوى ثري

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

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

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

لضمان التوافق مع الإصدارات السابقة من Android، تتوفّر واجهة برمجة التطبيقات هذه أيضًا في AndroidX، بدءًا من Core 1.7 وAppcompat 1.4، وننصحك باستخدامها عند تنفيذ هذه الوظيفة.

نظرة عامة

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

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

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

صورة تعرض واجهة برمجة التطبيقات الموحّدة المبسّطة
الشكل 3. تتيح لك واجهة برمجة التطبيقات الموحّدة استخدام واجهة برمجة تطبيقات واحدة تتوافق مع جميع آليات واجهة المستخدم.

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

التنفيذ

واجهة برمجة التطبيقات هي واجهة أداة معالجة حدث تتضمّن طريقة واحدة، وهي OnReceiveContentListener. لإتاحة الإصدارات القديمة من منصة Android، ننصحك باستخدام واجهة OnReceiveContentListener المتوافقة في مكتبة AndroidX Core.

لاستخدام واجهة برمجة التطبيقات، عليك تنفيذ أداة معالجة من خلال تحديد أنواع المحتوى التي يمكن لتطبيقك التعامل معها:

Kotlin

object MyReceiver : OnReceiveContentListener {
    val MIME_TYPES = arrayOf("image/*", "video/*")
    
    // ...
    
    override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? {
        TODO("Not yet implemented")
    }
}

Java

public class MyReceiver implements OnReceiveContentListener {
     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
     // ...
}

بعد تحديد جميع أنواع MIME للمحتوى التي يتوافق معها تطبيقك، نفِّذ بقية أجزاء أداة معالجة الأحداث:

Kotlin

class MyReceiver : OnReceiveContentListener {
    override fun onReceiveContent(view: View, contentInfo: ContentInfoCompat): ContentInfoCompat {
        val split = contentInfo.partition { item: ClipData.Item -> item.uri != null }
        val uriContent = split.first
        val remaining = split.second
        if (uriContent != null) {
            // App-specific logic to handle the URI(s) in uriContent.
        }
        // Return anything that your app didn't handle. This preserves the
        // default platform behavior for text and anything else that you aren't
        // implementing custom handling for.
        return remaining
    }

    companion object {
        val MIME_TYPES = arrayOf("image/*", "video/*")
    }
}

Java

 public class MyReceiver implements OnReceiveContentListener {
     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};

     @Override
     public ContentInfoCompat onReceiveContent(View view, ContentInfoCompat contentInfo) {
         Pair<ContentInfoCompat, ContentInfoCompat> split = contentInfo.partition(
                 item -> item.getUri() != null);
         ContentInfo uriContent = split.first;
         ContentInfo remaining = split.second;
         if (uriContent != null) {
             // App-specific logic to handle the URI(s) in uriContent.
         }
         // Return anything that your app didn't handle. This preserves the
         // default platform behavior for text and anything else that you aren't
         // implementing custom handling for.
         return remaining;
     }
 }

إذا كان تطبيقك يتيح المشاركة باستخدام Intent، يمكنك إعادة استخدام منطق تطبيقك الخاص للتعامل مع عناوين URI الخاصة بالمحتوى. إعادة أي بيانات متبقية إلى المندوب لتتولى المنصة معالجة تلك البيانات

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

Kotlin

class MyActivity : Activity() {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        val myInput = findViewById(R.id.my_input)
        ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, MyReceiver())
    }
}

Java

public class MyActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         // ...

         AppCompatEditText myInput = findViewById(R.id.my_input);
         ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, new MyReceiver());
     }
}

أذونات معرّف الموارد المنتظم (URI)

تمنح المنصة أذونات القراءة وتلغيها تلقائيًا لأي معرّفات موارد منتظمة (URI) خاصة بالمحتوى في الحمولة التي يتم تمريرها إلى OnReceiveContentListener.

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

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

طرق العرض المخصّصة

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

إذا كانت الفئة View تتجاوز طريقة onCreateInputConnection، استخدِم واجهة برمجة تطبيقات Jetpack InputConnectionCompat.createWrapper لضبط InputConnection.

إذا كانت الفئة View تتجاوز طريقة onTextContextMenuItem، عليك تفويضها إلى الفئة الرئيسية عند R.id.paste أو R.id.pasteAsPlainText.

المقارنة مع واجهة برمجة التطبيقات الخاصة بصورة لوحة المفاتيح

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

الجدول 1. الميزات ومستويات واجهة برمجة التطبيقات المتوافقة مع Jetpack
الإجراء أو الميزة متوافق مع واجهة برمجة التطبيقات الخاصة بصور لوحة المفاتيح متوافق مع واجهة برمجة التطبيقات الموحّدة
الإدراج من لوحة المفاتيح نعم (المستوى 13 لواجهة برمجة التطبيقات والإصدارات الأحدث) نعم (المستوى 13 لواجهة برمجة التطبيقات والإصدارات الأحدث)
الإدراج باستخدام "اللصق" من قائمة "النقر مع الاستمرار" لا نعم
الإدراج باستخدام السحب والإفلات لا نعم (المستوى 24 من واجهة برمجة التطبيقات والإصدارات الأحدث)
الجدول 2. الميزات ومستويات واجهة برمجة التطبيقات المتوافقة مع واجهات برمجة التطبيقات الأصلية
الإجراء أو الميزة متوافق مع واجهة برمجة التطبيقات الخاصة بصور لوحة المفاتيح متوافق مع واجهة برمجة التطبيقات الموحّدة
الإدراج من لوحة المفاتيح نعم (المستوى 25 لواجهة برمجة التطبيقات والإصدارات الأحدث) نعم (الإصدار 12 من نظام التشغيل Android والإصدارات الأحدث)
الإدراج باستخدام "اللصق" من قائمة "النقر مع الاستمرار" لا
الإدراج باستخدام السحب والإفلات لا