پشتیبانی از صفحه‌کلید تصویری

کاربران اغلب می‌خواهند بااستفاده از اموجی، برچسب، و انواع دیگر محتوای غنی ارتباط برقرار کنند. در نسخه‌های قبلی Android، صفحه‌کلیدهای نرم‌افزاری (که با نام ویرایشگرهای روش ورودی یا IME نیز شناخته می‌شوند) فقط می‌توانستند اموجی یونی‌کد به برنامه‌ها ارسال کنند. برای محتوای غنی، برنامه‌ها میاناهای برنامه‌سازی کاربردی مختص برنامه را می‌ساختند که نمی‌شد در برنامه‌های دیگر استفاده کرد یا از راه‌حل‌های جایگزین مثل ارسال تصاویر ازطریق کنش هم‌رسانی ساده یا بریده‌دان استفاده می‌کردند.

تصویری که صفحه‌کلیدی را نشان می‌دهد که از جستجوی تصویر پشتیبانی می‌کند
شکل ۱. نمونه‌ای از پشتیبانی صفحه‌کلید تصویری.

از Android 7.1 (سطح API‏ ۲۵)، کیت توسعه نرم‌افزار Android شامل Commit Content API است که روشی جهانی برای IMEها فراهم می‌کند تا تصاویر و سایر محتوای غنی را مستقیماً به ویرایشگر نوشتار در برنامه ارسال کنند. این API همچنین در v13 Support Library از نسخه ۲۵.۰.۰ دردسترس است. توصیه می‌کنیم از «کتابخانه پشتیبانی» استفاده کنید زیرا حاوی روش‌های کمکی است که پیاده‌سازی را ساده می‌کند.

با این میانای برنامه‌سازی کاربردی، می‌توانید برنامه‌های پیام‌رسانی بسازید که محتوای غنی را از هر صفحه‌کلیدی بپذیرند و همچنین صفحه‌کلیدهایی بسازید که بتوانند محتوای غنی را به هر برنامه‌ای ارسال کنند. صفحه‌کلید Google و برنامه‌هایی مانند پیام‌نگار Google از Commit Content API در Android 7.1 پشتیبانی می‌کنند، همان‌طور که در شکل ۱ نشان داده شده است.

این سند نحوه پیاده‌سازی Commit Content API را در IME و برنامه‌ها نشان می‌دهد.

روش کار

درج تصویر صفحه‌کلید به مشارکت IME و برنامه نیاز دارد. توالی زیر هر مرحله را در فرایند درج تصویر شرح می‌دهد:

  1. وقتی کاربر روی EditText تک‌ضرب می‌زند، ویرایشگر فهرستی از انواع محتوای MIME را که می‌پذیرد در EditorInfo.contentMimeTypes ارسال می‌کند.

  2. IME فهرست انواع پشتیبانی‌شده را می‌خواند و محتوا را در صفحه‌کلید نرم‌افزاری که ویرایشگر می‌تواند بپذیرد نمایش می‌دهد.

  3. وقتی کاربر تصویری را انتخاب می‌کند، IME commitContent() را فراخوانی می‌کند و InputContentInfo را به ویرایشگر ارسال می‌کند. فراخوانی commitContent() مشابه فراخوانی commitText() است، اما برای محتوای غنی. InputContentInfo حاوی URI است که محتوا را در ارائه‌دهنده محتوا شناسایی می‌کند.

این فرایند در شکل ۲ نشان داده شده است:

تصویری که توالی از «برنامه» به IME و برگشت به «برنامه» را نشان می‌دهد
شکل ۲. جریان برنامه به IME به برنامه.

افزودن پشتیبانی از تصویر به برنامه‌ها

برای پذیرش محتوای غنی از IME، برنامه باید به IME بگوید چه نوع محتوایی را می‌پذیرد و روش callback را که هنگام دریافت محتوا اجرا می‌شود مشخص کند. مثال زیر نشان می‌دهد که چگونه می‌توان یک EditText ایجاد کرد که تصاویر PNG را می‌پذیرد:

کاتلین

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
    }
}

جاوا

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 اضافه می‌کند. پیش‌از setContentMimeTypes() با super.onCreateInputConnection() تماس بگیرید.

  • callback هروقت IME محتوا را نهایی کند اجرا می‌شود. روش onCommitContent() به InputContentInfoCompat ارجاع دارد که حاوی شناسه منبع یکسان محتوا است.

    • اگر برنامه‌تان در سطح میانای برنامه‌سازی کاربردی ۲۵ یا بالاتر اجرا می‌شود و پرچم INPUT_CONTENT_GRANT_READ_URI_PERMISSION توسط IME تنظیم شده است، اجازه‌ها را درخواست و منتشر کنید. درغیراین‌صورت، ازقبل به شناسه منبع یکسان محتوا دسترسی دارید زیرا IME آن را اعطا کرده است یا ارائه‌دهنده محتوا دسترسی را محدود نمی‌کند. برای اطلاعات بیشتر، به افزودن پشتیبانی تصویر به IME مراجعه کنید.
  • createWrapper() InputConnection، EditorInfo اصلاح‌شده، و فراخوانی برگشتی را در InputConnection جدیدی می‌پیچد و آن را برمی‌گرداند.

موارد زیر شیوه‌های توصیه‌شده هستند:

  • ویرایشگرهایی که از محتوای غنی پشتیبانی نمی‌کنند، setContentMimeTypes() را فراخوانی نمی‌کنند و EditorInfo.contentMimeTypes خود را روی null تنظیم می‌کنند.

  • اگر نوع MIME مشخص‌شده در InputContentInfo با هیچ‌یک از انواع موردقبول ویرایشگرها مطابقت نداشته باشد، ویرایشگرها محتوا را نادیده می‌گیرند.

  • محتوای غنی بر موقعیت مکان‌نمای نوشتار تأثیری نمی‌گذارد و از آن تأثیر نمی‌پذیرد. ویرایشگران می‌توانند هنگام کار با محتوا، موقعیت مکان‌نما را نادیده بگیرند.

  • در روش OnCommitContentListener.onCommitContent() ویرایشگر، می‌توانید true را به‌صورت ناهمگام، حتی قبل‌از بار کردن محتوا، برگردانید.

  • برخلاف نوشتار که می‌تواند قبل‌از اعمال شدن در IME ویرایش شود، محتوای غنی بلافاصله اعمال می‌شود. اگر می‌خواهید به کاربران اجازه دهید محتوا را ویرایش یا حذف کنند، منطق را خودتان پیاده‌سازی کنید.

برای آزمایش کردن برنامه‌تان، مطمئن شوید دستگاه یا شبیه‌سازتان صفحه‌کلیدی دارد که می‌تواند محتوای غنی ارسال کند. می‌توانید از «صفحه‌کلید Google» در Android نسخه ۷.۱ یا بالاتر استفاده کنید.

افزودن پشتیبانی از تصویر به IME

IMEهایی که می‌خواهند محتوای غنی به برنامه‌ها ارسال کنند باید Commit Content API را پیاده‌سازی کنند، همان‌طور که در مثال زیر نشان داده شده است:

  • لغو کردن onStartInput() یا onStartInputView() و خواندن فهرست انواع محتوای پشتیبانی‌شده از ویرایشگر هدف. تکه‌کد زیر نشان می‌دهد که چگونه می‌توان بررسی کرد که آیا ویرایشگر هدف تصاویر GIF را می‌پذیرد یا نه.

کاتلین

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.
    }
}

جاوا

@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 را نشان می‌دهد.

کاتلین

// 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)
}

جاوا

// 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، احتمالاً باید ارائه‌دهنده محتوای خودتان را برای پاسخ دادن به درخواست‌های شناسه منبع یکسان محتوا پیاده‌سازی کنید. استثنا زمانی است که IME شما از محتوای ارائه‌دهندگان محتوای موجود مانند MediaStore پشتیبانی کند. برای کسب اطلاعات درباره ساخت ارائه‌دهندگان محتوا، به اسناد ارائه‌دهنده محتوا و ارائه‌دهنده فایل مراجعه کنید.

اگر ارائه‌دهنده محتوای خودتان را می‌سازید، توصیه می‌کنیم آن را با تنظیم کردن android:exported روی false صادر نکنید. به‌جای آن، با تنظیم android:grantUriPermission روی true، اعطای اجازه را در ارائه‌دهنده فعال کنید. سپس، IME شما می‌تواند هنگام ثبت محتوا، اجازه‌های دسترسی به شناسه منبع یکسان محتوا را اعطا کند. دو روش برای انجام این کار وجود دارد:

  • در Android 7.1 (سطح میانای برنامه‌سازی کاربردی ۲۵) و بالاتر، هنگام فراخوانی commitContent()، پارامتر پرچم را روی INPUT_CONTENT_GRANT_READ_URI_PERMISSION تنظیم کنید. سپس، شیء InputContentInfo که برنامه دریافت می‌کند می‌تواند با فراخوانی requestPermission() و releasePermission() اجازه‌های خواندن موقت را درخواست و منتشر کند.

  • در Android 7.0 (سطح میانای برنامه‌سازی کاربردی ۲۴) و پایین‌تر، INPUT_CONTENT_GRANT_READ_URI_PERMISSION نادیده گرفته می‌شود، بنابراین به‌صورت دستی اجازه دسترسی به محتوا را اعطا کنید. یک راه برای انجام این کار استفاده از grantUriPermission() است، اما می‌توانید سازوکار خودتان را که الزاماتتان را برآورده می‌کند پیاده‌سازی کنید.

برای آزمایش IME، مطمئن شوید دستگاه یا شبیه‌سازتان برنامه‌ای دارد که بتواند محتوای غنی دریافت کند. می‌توانید از برنامه Google Messenger در Android نسخه ۷.۱ یا بالاتر استفاده کنید.