קבלת תוכן עשיר

איור 1. ה-API המאוחד מספק מקום אחד לטיפול בתוכן נכנס, ללא קשר למנגנון הממשק המשתמש הספציפי, כמו הדבקה מהתפריט של לחיצה ארוכה או באמצעות גרירה ושחרור.

משתמשים אוהבים תמונות, סרטונים ותוכן אחר שיש בו אקספרסיביות, אבל לא תמיד קל להוסיף ולהעביר את התוכן הזה לאפליקציות. כדי להקל על האפליקציות לקבל תוכן עשיר, ב-Android 12 (רמת API ‏31) מוצג ממשק API מאוחד שמאפשר לאפליקציה לקבל תוכן מכל מקור: הלוח, המקלדת או גרירה.

אפשר לצרף ממשק, כמו OnReceiveContentListener, לרכיבי ממשק המשתמש ולקבל קריאה חוזרת כשמוסיפים תוכן באמצעות מנגנון כלשהו. פונקציית ה-callback הופכת למקום היחיד שבו הקוד שלכם יכול לטפל בקבלת כל התוכן, החל מטקסט פשוט ועד לטקסט עם סגנון, רכיבי Markup, תמונות, סרטונים, קובצי אודיו ועוד.

כדי לשמור על תאימות לאחור לגרסאות קודמות של Android, ממשק ה-API הזה זמין גם ב-AndroidX, החל מ-Core 1.7 ו-Appcompat 1.4. מומלץ להשתמש בהם כשמטמיעים את הפונקציונליות הזו.

סקירה כללית

בממשקי API קיימים אחרים, לכל מנגנון בממשק המשתמש – כמו התפריט של לחיצה ארוכה או גרירה – יש ממשק API תואם משלו. כלומר, צריך לשלב עם כל API בנפרד, ולהוסיף קוד דומה לכל מנגנון להוספת תוכן:

תמונה שבה מוצגות הפעולות השונות וה-API הרלוונטי לטמעה
איור 2. בעבר, אפליקציות הטמיעו ממשק API שונה לכל מנגנון של ממשק משתמש להוספת תוכן.

ממשק ה-API של OnReceiveContentListener מביא לידי ביטוי את נתיבי הקוד השונים האלה על ידי יצירת API יחיד להטמעה, כך שתוכלו להתמקד בלוגיקה הספציפית לאפליקציה שלכם ולתת לפלטפורמה לטפל בשאר הדברים:

תמונה שמציגה את ה-API המאוחד המפושט
איור 3. ה-API המאוחד מאפשר להטמיע ממשק API יחיד שתומך בכל המנגנונים של ממשק המשתמש.

משמעות הגישה הזו גם היא שכאשר מוסיפים לפלטפורמה דרכים חדשות להוספת תוכן, לא צריך לבצע שינויים נוספים בקוד כדי לתת תמיכה באפליקציה. ואם האפליקציה צריכה לבצע התאמה אישית מלאה בתרחיש לדוגמה מסוים, עדיין תוכלו להשתמש בממשקי ה-API הקיימים, שממשיכים לפעול באותו אופן.

הטמעה

ה-API הוא ממשק האזנה עם שיטה אחת, OnReceiveContentListener. כדי לתמוך בגרסאות ישנות יותר של פלטפורמת Android, מומלץ להשתמש בממשק התואם OnReceiveContentListener בספריית AndroidX Core.

כדי להשתמש ב-API, מטמיעים את המאזין ומציינים את סוגי התוכן שהאפליקציה יכולה לטפל בהם:

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 של תוכן. מחזירים את כל הנתונים הנותרים כדי להעביר את הטיפול בהם לפלטפורמה.

אחרי שמטמיעים את ה-listener, מגדירים אותו ברכיבי ממשק המשתמש המתאימים באפליקציה:

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 של תוכן בתוכן הייעודי (payload) שמוענק ל-OnReceiveContentListener.

בדרך כלל, האפליקציה מעבדת מזהי URI של תוכן בשירות או בפעילות. לעיבוד ממושך, השתמשו ב-WorkManager. כשמטמיעים את הקוד, מרחיבים את ההרשאות לפעילות או לשירות היעד על ידי העברת התוכן באמצעות Intent.setClipData והגדרת הדגל FLAG_GRANT_READ_URI_PERMISSION.

לחלופין, אפשר להשתמש בשרשור ברקע בתוך ההקשר הנוכחי כדי לעבד את התוכן. במקרה כזה, צריך לשמור הפניה לאובייקט payload שמקבל המאזינים כדי לוודא שההרשאות לא מבטלות מוקדם מדי את ההרשאות של הפלטפורמה.

תצוגות מותאמות אישית

אם באפליקציה שלכם נעשה שימוש בתת-סוג View בהתאמה אישית, חשוב לוודא שלא מתבצעת עקיפה של OnReceiveContentListener.

אם בכיתה View מתבצעת החלפה של השיטה onCreateInputConnection, צריך להשתמש ב-Jetpack API‏ InputConnectionCompat.createWrapper כדי להגדיר את InputConnection.

אם בכיתה View מתבצעת החלפה של השיטה onTextContextMenuItem, מעבירים את הטיפול ל-super כשפריט התפריט הוא R.id.paste או R.id.pasteAsPlainText.

השוואה ל-Keyboard Image API

אפשר להתייחס ל-API OnReceiveContentListener כגרסה הבאה של ממשק ה-API הקיים לתמונות של מקלדת. ה-API המאוחד הזה תומך בפונקציונליות של ממשק ה-API של תמונת המקלדת ובכמה תכונות נוספות. התאימות למכשירים ולתכונות משתנה בהתאם לשימוש בספריית Jetpack או בממשקי ה-API המקוריים מ-Android SDK.

טבלה 1. תכונות נתמכות ורמות API שנתמכות ב-Jetpack.
פעולה או תכונה נתמך על ידי ממשק API של תמונה של מקלדת נתמכים על ידי API מאוחד
הוספה מהמקלדת כן (רמת API 13 ומעלה) כן (רמת API 13 ומעלה)
הוספה באמצעות הדבקה מהתפריט של הלחיצה הארוכה לא כן
הוספה באמצעות גרירה ושחרור לא כן (רמת API 24 ואילך)
טבלה 2. התכונות והרמות הנתמכות של ממשקי API מקומיים.
פעולה או תכונה נתמך על ידי ממשק API של תמונה של מקלדת נתמכים על ידי API מאוחד
הוספה מהמקלדת כן (רמת API 25 ואילך) כן (Android מגרסה 12 ואילך)
הוספה באמצעות הדבקה מהתפריט של הלחיצה הארוכה לא
הוספה באמצעות גרירה ושחרור לא