Gboard: Enable GIF insertion on EditText

孤街醉人 提交于 2019-12-03 06:04:28

Image Keyboard Support

Users often want to communicate with emojis, stickers, and other kinds of rich
content. In previous versions of Android, soft keyboards (also known as input
method editors or IMEs) could send only unicode emoji to apps. For rich
content, apps had to either build app-specific APIs that couldn't be used in
other apps or use workaround like sending images through Easy Share Action or the
clipboard.

How it works

Keyboard image insertion requires participation from both the IME and the app. The following sequence describes each step in the image insertion process:

When the user taps on an EditText, the editor sends a list of MIME content types that it accepts in EditorInfo.contentMimeTypes.

The IME reads the list of supported types and displays content in the soft keyboard that the editor can accept.

When the user selects an image, the IME calls commitContent() and sends an InputContentInfo to the editor. The commitContent() call is analogous to the commitText() call, but for rich content. InputContentInfo contains an URI that identifies the content in a content provider. Your app can then request permission and read the content from the URI.

To accept rich content from IMEs, apps must tell IMEs what content types it 
accepts and specify a callbackup method that is executed when content is
received. The following example demonstrates how to create an EditText that
accept PNG images:
EditText editText = new EditText(this) {
    @Override
    public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
        final InputConnection ic = super.onCreateInputConnection(editorInfo);
        EditorInfoCompat.setContentMimeTypes(editorInfo,
                new String [] {"image/png"});

        final InputConnectionCompat.OnCommitContentListener callback =
            new InputConnectionCompat.OnCommitContentListener() {
                @Override
                public boolean onCommitContent(InputContentInfoCompat inputContentInfo,
                        int flags, Bundle opts) {
                    // read and display inputContentInfo asynchronously
                    if (BuildCompat.isAtLeastNMR1() && (flags &
                        InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
                        try {
                            inputContentInfo.requestPermission();
                        }
                        catch (Exception e) {
                            return false; // return false if failed
                        }
                    }

                    // read and display inputContentInfo asynchronously.
                    // call inputContentInfo.releasePermission() as needed.

                    return true;  // return true if succeeded
                }
            };
        return InputConnectionCompat.createWrapper(ic, editorInfo, callback);
    }
};

Here is Full Documentation Reference

https://developer.android.com/guide/topics/text/image-keyboard.html#how_it_works

Adding Image Support to IMEs

IMEs that want to send rich content to apps must implement the Commit Content API as shown below:

Override onStartInput() or onStartInputView() and read the list of supported content types from the target editor. The following code snippet shows how to check whether the target editor accepts GIF images.

@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 corresponding content
    } else {
        // the target editor does not support GIFs. disable corresponding content
    }
}

Commit content to the app when the users selects an image. Avoid calling commitContent() when there is any composing text because it might cause the editor to lose focus. The following code snippet shows how to commit a GIF image.

/**
 * 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"}));
    InputConnection inputConnection = getCurrentInputConnection();
    EditorInfo editorInfo = getCurrentInputEditorInfo();
    Int flags = 0;
    if (android.os.Build.VERSION.SDK_INT >= 25) {
        flags |= InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
    }
    InputConnectionCompat.commitContent(
            inputConnection, editorInfo, inputContentInfo, flags, opts);
}

Here is Full Documentation

https://developer.android.com/guide/topics/text/image-keyboard.html#adding_image_support_to_imes

I found the solution below from somewhere. Hope it will help everyone for sure.

import android.content.Context;
import android.os.Bundle;
import android.support.v13.view.inputmethod.EditorInfoCompat;
import android.support.v13.view.inputmethod.InputConnectionCompat;
import android.support.v13.view.inputmethod.InputContentInfoCompat;
import android.support.v4.os.BuildCompat;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;


public class MyEditText extends android.support.v7.widget.AppCompatEditText {

    private String[] imgTypeString;
    private KeyBoardInputCallbackListener keyBoardInputCallbackListener;

    public MyEditText(Context context) {
        super(context);
        initView();
    }

    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    private void initView() {
        imgTypeString = new String[]{"image/png",
                "image/gif",
                "image/jpeg",
                "image/webp"};
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        final InputConnection ic = super.onCreateInputConnection(outAttrs);
        EditorInfoCompat.setContentMimeTypes(outAttrs,
                imgTypeString);
        return InputConnectionCompat.createWrapper(ic, outAttrs, callback);
    }


    final InputConnectionCompat.OnCommitContentListener callback =
            new InputConnectionCompat.OnCommitContentListener() {
                @Override
                public boolean onCommitContent(InputContentInfoCompat inputContentInfo,
                                               int flags, Bundle opts) {

                    // read and display inputContentInfo asynchronously
                    if (BuildCompat.isAtLeastNMR1() && (flags &
                            InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
                        try {
                            inputContentInfo.requestPermission();
                        } catch (Exception e) {
                            return false; // return false if failed
                        }
                    }
                    boolean supported = false;
                    for (final String mimeType : imgTypeString) {
                        if (inputContentInfo.getDescription().hasMimeType(mimeType)) {
                            supported = true;
                            break;
                        }
                    }
                    if (!supported) {
                        return false;
                    }

                    if (keyBoardInputCallbackListener != null) {
                        keyBoardInputCallbackListener.onCommitContent(inputContentInfo, flags, opts);
                    }
                    return true;  // return true if succeeded
                }
            };

    public interface KeyBoardInputCallbackListener {
        void onCommitContent(InputContentInfoCompat inputContentInfo,
                             int flags, Bundle opts);
    }

    public void setKeyBoardInputCallbackListener(KeyBoardInputCallbackListener keyBoardInputCallbackListener) {
        this.keyBoardInputCallbackListener = keyBoardInputCallbackListener;
    }

    public String[] getImgTypeString() {
        return imgTypeString;
    }

    public void setImgTypeString(String[] imgTypeString) {
        this.imgTypeString = imgTypeString;
    }
}

If you want an EditText with this functionality built in, check out my library, RichContentEditText: https://github.com/GregoryConrad/RichContentEditText

It painlessly allows rich content (images and other files) to be transferred to the app from the IME. Here's some sample code:

XML

<?xml version="1.0" encoding="utf-8"?>
<!-- Root layout can be anything. Just make sure to include xmlns:app line. -->
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!-- The RichContentEditText -->
    <!-- Notice app:allowedMimeTypes="images"; it is what accepts certain mime types
             (you can do this programmatically too) -->
    <com.gsconrad.richcontentedittext.RichContentEditText
        android:id="@+id/rich_content_edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/rich_content_edit_text_hint"
        android:inputType="text"
        app:allowedMimeTypes="images" />
</android.support.constraint.ConstraintLayout>

Java

// MainActivity.java
private void setupRichContentEditText() {
    RichContentEditText editText = findViewById(R.id.rich_content_edit_text);
    // The following line sets the listener that is called when rich content is received
    editText.setOnRichContentListener(new RichContentEditText.OnRichContentListener() {
        // Called when a keyboard sends rich content
        @Override
        public void onRichContent(Uri contentUri, ClipDescription description) {
            if (description.getMimeTypeCount() > 0) {
                final String fileExtension = MimeTypeMap.getSingleton()
                        .getExtensionFromMimeType(description.getMimeType(0));
                final String filename = "filenameGoesHere." + fileExtension;
                File richContentFile = new File(getFilesDir(), filename);
                // See the library example app for an implementation of writeToFileFromContentUri
                if (!writeToFileFromContentUri(richContentFile, contentUri)) {
                    // We are in the background right now, make sure to run this in the foreground
                    Toast.makeText(MainActivity.this,
                            R.string.rich_content_copy_failure, Toast.LENGTH_LONG).show();
                } else {
                    // We are in the background right now, make sure to run this in the foreground
                    WebView displayView = findViewById(R.id.display_view);
                    displayView.loadUrl("file://" + richContentFile.getAbsolutePath());
                }
            }
        }
    });
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!