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

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

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

با شروع Android 7.1 (سطح API 25)، Android SDK شامل Commit Content API است که راهی جهانی برای IME ها فراهم می کند تا تصاویر و سایر محتوای غنی را مستقیماً به یک ویرایشگر متن در یک برنامه ارسال کنند. API همچنین در نسخه 25.0.0 در کتابخانه پشتیبانی v13 موجود است. ما استفاده از کتابخانه پشتیبانی را توصیه می کنیم زیرا حاوی روش های کمکی است که پیاده سازی را ساده می کند.

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

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

چگونه کار می کند

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

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

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

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

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

تصویری که دنباله ای از Application به IME و بازگشت به Application را نشان می دهد
شکل 2. کاربرد در IME به جریان برنامه.

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

برای پذیرش محتوای غنی از IME، یک برنامه باید به IME ها بگوید که چه نوع محتوایی را می پذیرد و یک روش بازگشت به تماس را مشخص کند که هنگام دریافت محتوا اجرا می شود. مثال زیر نحوه ایجاد یک 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() را فراخوانی کنید.

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

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

اقدامات زیر توصیه می شود:

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

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

  • محتوای غنی بر موقعیت مکان نما متن تأثیر نمی گذارد و تحت تأثیر قرار نمی گیرد. ویراستاران می توانند موقعیت مکان نما را هنگام کار با محتوا نادیده بگیرند.

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

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

برای آزمایش برنامه‌تان، مطمئن شوید که دستگاه یا شبیه‌سازتان صفحه‌کلیدی دارد که می‌تواند محتوای غنی ارسال کند. می توانید از صفحه کلید Google در اندروید 7.1 یا بالاتر استفاده کنید.

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

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

  • در اندروید 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 خود، مطمئن شوید که دستگاه یا شبیه ساز شما دارای برنامه ای است که می تواند محتوای غنی را دریافت کند. می توانید از برنامه مسنجر گوگل در اندروید 7.1 یا بالاتر استفاده کنید.