משתמשים אוהבים תמונות, סרטונים ותוכן אחר שמאפשר להם לבטא את עצמם, אבל לא תמיד קל להוסיף ולהעביר את התוכן הזה באפליקציות. כדי להקל על אפליקציות לקבל תוכן עשיר, ב-Android 12 (רמת API 31) יש ממשק API מאוחד שמאפשר לאפליקציה לקבל תוכן מכל מקור: לוח, מקלדת או גרירה.
אתם יכולים לצרף ממשק, כמו OnReceiveContentListener, לרכיבי ממשק משתמש ולקבל קריאה חוזרת (callback) כשתוכן מוכנס דרך כל מנגנון. הקריאה החוזרת (callback) הופכת למקום היחיד שבו הקוד שלכם מטפל בקבלת כל התוכן, מטקסט פשוט ומעוצב ועד לתגי עיצוב, תמונות, סרטונים, קובצי אודיו ועוד.
כדי לאפשר תאימות לדור קודם של גרסאות Android, ה-API הזה זמין גם ב-AndroidX, החל מ-Core 1.7 ומ-Appcompat 1.4. אנחנו ממליצים להשתמש בהם כשמטמיעים את הפונקציונליות הזו.
סקירה כללית
בממשקי API קיימים אחרים, לכל מנגנון של ממשק משתמש – כמו תפריט לחיצה ארוכה או גרירה – יש ממשק API משלו. זה אומר שצריך לשלב כל API בנפרד, ולהוסיף קוד דומה לכל מנגנון שמכניס תוכן:
OnReceiveContentListener API מאחד את נתיבי הקוד השונים האלה על ידי יצירת API יחיד להטמעה, כך שתוכלו להתמקד בלוגיקה הספציפית לאפליקציה שלכם ולאפשר לפלטפורמה לטפל בשאר:
הגישה הזו גם אומרת שכאשר מתווספות לפלטפורמה דרכים חדשות להוספת תוכן, לא צריך לבצע שינויים נוספים בקוד כדי להפעיל תמיכה באפליקציה. ואם האפליקציה צריכה להטמיע התאמה אישית מלאה לתרחיש שימוש מסוים, עדיין אפשר להשתמש בממשקי ה-API הקיימים, שממשיכים לפעול באותו אופן.
הטמעה
ה-API הוא ממשק listener עם method אחת, 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 של תוכן. להחזיר את שאר הנתונים כדי להעביר את הטיפול בהם לפלטפורמה.
אחרי שמטמיעים את המאזין, מגדירים אותו ברכיבים המתאימים בממשק המשתמש באפליקציה:
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, צריך להשתמש ב-API של Jetpack InputConnectionCompat.createWrapper כדי להגדיר את InputConnection.
אם המחלקה View מבטלת את השיטה onTextContextMenuItem, צריך להקצות ל-super כשהפריט בתפריט הוא R.id.paste או R.id.pasteAsPlainText.
השוואה ל-API של תמונות במקלדת
אפשר להתייחס ל-API של OnReceiveContentListener כגרסה הבאה של ה-API הקיים של תמונות במקלדת. ה-API המאוחד הזה תומך בפונקציונליות של ה-API של תמונות המקלדת, וגם בכמה תכונות נוספות. התאימות של המכשיר והתכונות משתנה בהתאם לשימוש בספריית Jetpack או בממשקי ה-API המקוריים מ-Android SDK.
| פעולה או תכונה | נתמך על ידי API של תמונות במקלדת | נתמך על ידי Unified API |
|---|---|---|
| הוספה מהמקלדת | כן (רמת API 13 ומעלה) | כן (רמת API 13 ומעלה) |
| הוספה באמצעות הדבקה מהתפריט של הלחיצה הארוכה | לא | כן |
| הוספה באמצעות גרירה ושחרור | לא | כן (רמת API 24 ומעלה) |
| פעולה או תכונה | נתמך על ידי API של תמונות במקלדת | נתמך על ידי Unified API |
|---|---|---|
| הוספה מהמקלדת | כן (רמת API 25 ומעלה) | כן (Android מגרסה 12 ואילך) |
| הוספה באמצעות הדבקה מהתפריט של הלחיצה הארוכה | לא | |
| הוספה באמצעות גרירה ושחרור | לא |