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

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

يفضّل المستخدمون الصور والفيديوهات والمحتوى التعبيري الآخر، ولكن ليس من السهل دائمًا إدراج هذا المحتوى ونقله في التطبيقات. لتسهيل تلقّي التطبيقات للمحتوى الوافي، يقدّم Android 12 (مستوى واجهة برمجة التطبيقات 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;
     }
 }

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

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

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)

يمنح النظام الأساسي أذونات القراءة ويُصدرها تلقائيًا لأي معرّفات موارد منتظمة للمحتوى في الحمولة التي يتم تمريرها إلى OnReceiveContentListener.

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

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

العروض المخصصة

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

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

إذا كانت فئة View تلغي طريقة onTextContextMenuItem، يجب تفويض الفئة الفائقة عندما يكون عنصر القائمة R.id.paste أو R.id.pasteAsPlainText.

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

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

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