תמיכה במקלדת תמונה

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

תמונה שמוצגת בה מקלדת שתומכת בחיפוש באמצעות תמונות
איור 1. דוגמה לתמיכה במקלדת תמונה.

החל מ-Android 7.1 (רמת API 25), Android SDK כולל את האפשרות Commit Content API, שמספק דרך אוניברסלית לשליחת תמונות לרכיבי IME תוכן עשיר ישירות לכלי לעריכת טקסט באפליקציה. ה-API זמין גם ספריית התמיכה v13 נכון לגרסה 25.0.0. מומלץ להשתמש ב'תמיכה' ספרייה כי היא מכילה שיטות מסייעות שמפשטות את ההטמעה.

באמצעות ה-API הזה, תוכלו לפתח אפליקציות להעברת הודעות שמקבלות תוכן עשיר מכל מקלדת ומקלדות שיכולות לשלוח תוכן עשיר לכל אפליקציה. פלטפורמת Google מקלדת ואפליקציות כמו Messages של Google תומכים ב-Commit Content API ב-Android 7.1, כפי שמוצג באיור 1.

המסמך הזה מראה איך להטמיע את Commit Content API ברכיבי IME באפליקציות.

איך זה עובד

כדי להוסיף תמונה במקלדת, נדרשת השתתפות ב-IME ומהאפליקציה. ברצף הבא מתאר כל שלב בתהליך הוספת התמונה:

  1. כשהמשתמש מקיש על EditText, העורך שולח רשימה של סוגי תוכן MIME שהוא מקבל EditorInfo.contentMimeTypes

  2. ה-IME קורא את רשימת הסוגים הנתמכים ומציג תוכן שהעורך יכול לקבל.

  3. כאשר המשתמש בוחר תמונה, ה-IME קורא commitContent() ושולחת InputContentInfo לעורך. הקריאה commitContent() מקבילה commitText(), אבל כשמדובר בתוכן עשיר. InputContentInfo מכיל URI מזהה את התוכן בתוכן ספק.

התהליך הזה מתואר באיור 2:

תמונה שמציגה את הרצף מ'אפליקציה' ל-IME וחוזרת אל 'אפליקציה'
איור 2. יישום ב-IME לזרימת האפליקציה.

הוספת תמיכה בתמונות לאפליקציות

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

Kotlin

var editText: EditText = object : EditText(this) {
    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
        var ic = super.onCreateInputConnection(outAttrs)
        EditorInfoCompat.setContentMimeTypes(outAttrs, arrayOf("image/png"))
        val mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this)
        if (mimeTypes != null) {
            EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes)
            ic = InputConnectionCompat.createWrapper(this, ic, outAttrs)
        }
        return ic
    }
}

Java

EditText editText = new EditText(this) {
    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        InputConnection ic = super.onCreateInputConnection(outAttrs);
        EditorInfoCompat.setContentMimeTypes(outAttrs, new String[]{"image/png"});
        String[] mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this);
        if (mimeTypes != null) {
            EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes);
            ic = InputConnectionCompat.createWrapper(this, ic, outAttrs);
        }
        return ic;
    }
};

הסבר נוסף:

  • בדוגמה הזו נשתמש בספריית התמיכה, לכן יש כמה הפניות android.support.v13.view.inputmethod במקום android.view.inputmethod

  • בדוגמה הזו נוצר EditText וביטול שלו onCreateInputConnection(EditorInfo) כדי לשנות את InputConnection. InputConnection הוא ערוץ התקשורת בין IME אפליקציה שמקבלת את הקלט שלה.

  • השיחה super.onCreateInputConnection() שומרת על ההתנהגות המובנית – שליחה וקבלה של טקסט – נותן לכם הפניה ל-InputConnection.

  • setContentMimeTypes() מוסיף רשימה של סוגי MIME נתמכים EditorInfo שיחת טלפון super.onCreateInputConnection() לפני setContentMimeTypes().

  • callback מבוצעת בכל פעם שה-IME מבצע תוכן. השיטה onCommitContent() כולל התייחסות ל InputContentInfoCompat, שמכיל URI של תוכן.

    • שליחת בקשה ואישור הרשאות אם האפליקציה פועלת ברמת API 25 ומעלה, INPUT_CONTENT_GRANT_READ_URI_PERMISSION מוגדר על ידי ה-IME. אחרת, כבר יש לך גישה לתוכן URI מכיוון שהוא מוענק על ידי ה-IME או מפני שספק התוכן לא מגבילה את הגישה. למידע נוסף, אפשר לעיין בהוספת תמיכה בתמונות אל IME
  • createWrapper() כולל את InputConnection, את EditorInfo שהשתנה ואת הקריאה החוזרת למכשיר InputConnection חדש, ומחזירה אותו.

אלה השיטות המומלצות:

  • עורכים שלא תומכים בתוכן עשיר לא מבצעים שיחה setContentMimeTypes(), והם משאירים את קבוצת EditorInfo.contentMimeTypes שלהם אל null.

  • העורכים מתעלמים מהתוכן אם סוג ה-MIME שצוין ב-InputContentInfo לא תואם לאף אחד מהסוגים שהוא מקבל.

  • תוכן עשיר לא משפיע על מיקום הטקסט ולא מושפע ממנו הסמן. עורכים יכולים להתעלם ממיקום הסמן כשהם עובדים עם תוכן.

  • בתוך OnCommitContentListener.onCommitContent(), אפשר להחזיר true באופן אסינכרוני, אפילו לפני טעינת התוכן.

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

כדי לבדוק את האפליקציה, צריך לוודא שלמכשיר או לאמולטור יש מקלדת שיכולה לשלוח תוכן עשיר. אפשר להשתמש במקלדת Google במערכת Android מגרסה 7.1 ואילך.

הוספת תמיכה בתמונות ל-IME

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

  • שינוי מברירת המחדל onStartInput() או onStartInputView() ולקרוא את רשימת סוגי התוכן הנתמכים מהיעד עם הרשאת עריכה. קטע הקוד הבא מראה איך לבדוק אם העורך מקבל תמונות בפורמט GIF.

Kotlin

override fun onStartInputView(editorInfo: EditorInfo, restarting: Boolean) {
    val mimeTypes: Array<String> = EditorInfoCompat.getContentMimeTypes(editorInfo)

    val gifSupported: Boolean = mimeTypes.any {
        ClipDescription.compareMimeTypes(it, "image/gif")
    }

    if (gifSupported) {
        // The target editor supports GIFs. Enable the corresponding content.
    } else {
        // The target editor doesn't support GIFs. Disable the corresponding
        // content.
    }
}

Java

@Override
public void onStartInputView(EditorInfo info, boolean restarting) {
    String[] mimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);

    boolean gifSupported = false;
    for (String mimeType : mimeTypes) {
        if (ClipDescription.compareMimeTypes(mimeType, "image/gif")) {
            gifSupported = true;
        }
    }

    if (gifSupported) {
        // The target editor supports GIFs. Enable the corresponding content.
    } else {
        // The target editor doesn't support GIFs. Disable the corresponding
        // content.
    }
}
  • שמירת תוכן לאפליקציה כשהמשתמש בוחר תמונה. לא להתקשר commitContent() כשיש טקסט בכתיבה, כי הוא עלול לגרום לעורך לאבד את המיקוד. קטע הקוד הבא מראה איך כדי לשמור תמונה בפורמט GIF.

Kotlin

// Commits a GIF image.

// @param contentUri = Content URI of the GIF image to be sent.
// @param imageDescription = Description of the GIF image to be sent.

fun commitGifImage(contentUri: Uri, imageDescription: String) {
    val inputContentInfo = InputContentInfoCompat(
            contentUri,
            ClipDescription(imageDescription, arrayOf("image/gif")),
            null
    )
    val inputConnection = currentInputConnection
    val editorInfo = currentInputEditorInfo
    var flags = 0
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
        flags = flags or InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
    }
    InputConnectionCompat.commitContent(inputConnection, editorInfo, inputContentInfo, flags, null)
}

Java

// Commits a GIF image.

// @param contentUri = Content URI of the GIF image to be sent.
// @param imageDescription = Description of the GIF image to be sent.

public static void commitGifImage(Uri contentUri, String imageDescription) {
    InputContentInfoCompat inputContentInfo = new InputContentInfoCompat(
            contentUri,
            new ClipDescription(imageDescription, new String[]{"image/gif"}),
            null
    );
    InputConnection inputConnection = getCurrentInputConnection();
    EditorInfo editorInfo = getCurrentInputEditorInfo();
    Int flags = 0;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
        flags |= InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
    }
    InputConnectionCompat.commitContent(
            inputConnection, editorInfo, inputContentInfo, flags, null);
}

כמחבר/ת IME, סביר להניח שעליך להטמיע ספק תוכן משלך כדי להגיב לבקשות URI של תוכן. יוצא הדופן הוא אם ה-IME שלך תומך בתוכן מספקי תוכן קיימים כמו MediaStore לקבלת מידע על של ספקי תוכן, ראו ספק וקובץ הספק.

אם אתם מפתחים ספק תוכן משלכם, מומלץ לא לייצא אותו על ידי הגדרה android:exported עד false במקום זאת, אפשר להפעיל את הענקת ההרשאות בספק באמצעות הגדרה android:grantUriPermission אל true. לאחר מכן, ה-IME יכול להעניק הרשאות גישה ל-URI של התוכן כאשר שבו התוכן מחויב. ניתן לעשות זאת בשתי דרכים:

  • ב-Android 7.1 (רמת API 25) ואילך, כשמבצעים קריאה ל-commitContent(), מגדירים את הפרמטר של הדגל INPUT_CONTENT_GRANT_READ_URI_PERMISSION. לאחר מכן, האובייקט InputContentInfo שהאפליקציה מקבלת יכול לבקש מתן הרשאות קריאה זמניות באמצעות התקשרות requestPermission() וגם releasePermission().

  • ב-Android 7.0 (רמת API 24) ומטה, המערכת מתעלמת מ-INPUT_CONTENT_GRANT_READ_URI_PERMISSION, לכן הענקת גישה באופן ידני הרשאה לתוכן. דרך אחת לעשות זאת היא grantUriPermission(), אבל אתם יכולים להטמיע מנגנון משלכם שעומד בדרישות שלך.

כדי לבדוק את ה-IME, ודאו שלמכשיר או לאמולטור יש אפליקציה שיכולה לקבל תוכן עשיר. ניתן להשתמש באפליקציית Google Messenger במערכת Android 7.1 ומעלה.