Hỗ trợ bàn phím hình ảnh

Người dùng thường muốn giao tiếp bằng biểu tượng cảm xúc, hình dán và các loại nội dung. Trong các phiên bản trước của Android, bàn phím mềm (còn được gọi là trình chỉnh sửa phương thức nhập, hoặc IME chỉ có thể gửi biểu tượng cảm xúc Unicode đến ứng dụng. Đối với nội dung đa dạng thức, các ứng dụng được tạo API dành riêng cho ứng dụng không dùng được trong các ứng dụng khác hoặc đã dùng để giải quyết như gửi hình ảnh thông qua hành động chia sẻ đơn giản hoặc bảng nhớ tạm.

Hình ảnh minh hoạ một bàn phím có hỗ trợ tính năng tìm kiếm hình ảnh
Hình 1. Ví dụ về tính năng hỗ trợ bàn phím hình ảnh.

Kể từ Android 7.1 (API cấp 25), SDK Android sẽ bao gồm mã Cam kết Content API cung cấp một cách thức phổ biến để IME gửi hình ảnh và các nội dung đa dạng thức trực tiếp vào trình chỉnh sửa văn bản trong ứng dụng. API này cũng có trong Thư viện hỗ trợ v13 kể từ bản sửa đổi 25.0.0. Bạn nên sử dụng dịch vụ Hỗ trợ Thư viện vì chứa các phương thức trợ giúp giúp đơn giản hoá quá trình triển khai.

Với API này, bạn có thể tạo ứng dụng nhắn tin chấp nhận nội dung đa dạng thức từ bất kỳ bàn phím cũng như bàn phím có thể gửi nội dung đa dạng thức đến bất kỳ ứng dụng nào. Nhóm Google Bàn phím và các ứng dụng như Tin nhắn của Google hỗ trợ Commit Content API trong Android 7.1, như được hiển thị trong hình 1.

Tài liệu này trình bày cách triển khai Commit Content API trong cả IME và của chúng tôi.

Cách hoạt động

Tính năng chèn hình ảnh bàn phím cần có sự tham gia của IME và ứng dụng. Chiến lược phát hành đĩa đơn trình tự sau đây mô tả từng bước trong quy trình chèn hình ảnh:

  1. Khi người dùng nhấn vào một EditText, trình chỉnh sửa gửi danh sách các loại nội dung MIME mà trình chỉnh sửa chấp nhận EditorInfo.contentMimeTypes.

  2. IME đọc danh sách các loại được hỗ trợ và hiển thị nội dung trong bàn phím mềm mà trình chỉnh sửa có thể chấp nhận.

  3. Khi người dùng chọn một hình ảnh, IME sẽ gọi commitContent() và gửi một InputContentInfo cho trình chỉnh sửa. Lệnh gọi commitContent() tương tự như commitText() cuộc gọi, nhưng đối với nội dung đa dạng thức. InputContentInfo chứa một URI xác định nội dung trong một nội dung Google Cloud.

Quá trình này được mô tả trong hình 2:

Hình ảnh hiển thị trình tự từ Application đến IME và quay lại Application
Hình 2. Ứng dụng cho IME vào luồng ứng dụng.

Thêm tính năng hỗ trợ hình ảnh vào ứng dụng

Để chấp nhận nội dung đa dạng thức từ IME, ứng dụng phải cho IME biết loại nội dung nào chấp nhận và chỉ định phương thức gọi lại được thực thi khi nhận được nội dung. Ví dụ sau minh hoạ cách tạo EditText chấp nhận PNG hình ảnh:

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

Sau đây là nội dung giải thích cụ thể hơn:

Sau đây là các phương pháp được đề xuất:

  • Các trình chỉnh sửa không hỗ trợ nội dung đa dạng thức không gọi setContentMimeTypes() và họ để EditorInfo.contentMimeTypes thiết lập của mình đến null.

  • Trình chỉnh sửa bỏ qua nội dung nếu loại MIME được chỉ định trong InputContentInfo không khớp với bất kỳ loại nào được chấp nhận.

  • Nội dung đa dạng thức không ảnh hưởng và không chịu ảnh hưởng của vị trí của văn bản con trỏ. Trình chỉnh sửa có thể bỏ qua vị trí con trỏ khi làm việc với nội dung.

  • Trong phần trình biên tập OnCommitContentListener.onCommitContent(), bạn có thể trả về true một cách không đồng bộ, thậm chí trước khi tải nội dung.

  • Không giống như văn bản (có thể chỉnh sửa trong IME trước khi gửi ở dạng văn bản đa dạng thức) được cam kết ngay lập tức. Nếu bạn muốn cho phép người dùng chỉnh sửa hoặc xoá hãy tự triển khai logic.

Để kiểm thử ứng dụng, hãy đảm bảo thiết bị hoặc trình mô phỏng của bạn có bàn phím có thể gửi nội dung đa dạng thức. Bạn có thể sử dụng Bàn phím Google trong Android 7.1 trở lên.

Thêm tính năng hỗ trợ hình ảnh vào IME

Những IME muốn gửi nội dung đa dạng thức đến ứng dụng phải triển khai Nội dung cam kết như trong ví dụ sau:

  • Ghi đè onStartInput() hoặc onStartInputView() và đọc danh sách các loại nội dung được hỗ trợ trong mục tiêu trình chỉnh sửa. Đoạn mã sau đây cho biết cách kiểm tra xem trình chỉnh sửa mục tiêu có chấp nhận hình ảnh GIF hay không.

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

  • Xác nhận nội dung cho ứng dụng khi người dùng chọn một hình ảnh. Tránh gọi điện commitContent() khi có văn bản đang được soạn vì phần tử này có thể làm cho trình chỉnh sửa mất tiêu điểm. Đoạn mã sau đây cho biết cách xác nhận một hình ảnh 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);
}

Là tác giả IME, nhiều khả năng bạn phải triển khai trình cung cấp nội dung của riêng mình để phản hồi các yêu cầu URI nội dung. Trường hợp ngoại lệ là nếu IME của bạn hỗ trợ nội dung từ các nhà cung cấp nội dung hiện tại như MediaStore. Để biết thông tin về nhà cung cấp nội dung, hãy xem nội dung nhà cung cấptệp nhà cung cấp của chúng tôi.

Nếu đang xây dựng nhà cung cấp nội dung của riêng mình, bạn không nên xuất nhà cung cấp đó theo chế độ cài đặt android:exported đến false. Thay vào đó, hãy bật tính năng cấp quyền trong trình cung cấp bằng cách cài đặt android:grantUriPermission đến true. Sau đó, IME của bạn có thể cấp quyền truy cập URI nội dung khi nội dung được gửi. Có hai cách để thực hiện việc này:

  • Trên Android 7.1 (API cấp 25) trở lên, khi gọi commitContent(), đặt thông số gắn cờ thành INPUT_CONTENT_GRANT_READ_URI_PERMISSION. Sau đó, đối tượng InputContentInfo mà ứng dụng nhận được có thể yêu cầu và huỷ bỏ quyền đọc tạm thời bằng cách gọi requestPermission()releasePermission().

  • Trên Android 7.0 (API cấp 24) trở xuống, INPUT_CONTENT_GRANT_READ_URI_PERMISSION bị bỏ qua nên hãy cấp theo cách thủ công quyền đối với nội dung. Một cách để thực hiện việc này là dùng grantUriPermission(), nhưng bạn có thể triển khai cơ chế riêng đáp ứng yêu cầu của riêng bạn.

Để kiểm thử IME, hãy đảm bảo thiết bị hoặc trình mô phỏng của bạn có ứng dụng có thể nhận nội dung đa dạng thức. Bạn có thể sử dụng ứng dụng Google Messenger trong Android 7.1 trở lên.