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

الشكل 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;
     }
 }

إذا كان تطبيقك يتيح حاليًا المشاركة باستخدام الأهداف، يمكنك إعادة استخدام المنطق الخاص بتطبيقك لمعالجة عناوين 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 أو واجهات برمجة التطبيقات الأصلية من Android SDK.

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