5

I am trying to make a chat in jetpack compose, and I want to be able to use the standard gif keyboard on my samsung to send a gif.

When I click a GIF on a normal TextField, I am currently getting a message "Can't enter this content here"

I found something called Commit Content API which should make it possible to add a GIF in a old EditText so I am trying that inside an AndroidView, Now I dont get the error message anymore, But I also don't have a clue where the GIF is and how it is represented.

AndroidView(factory = {
    val editText = @SuppressLint("AppCompatCustomView")
    object : EditText(it) {

        override fun setOnReceiveContentListener(
            mimeTypes: Array<out String>?,
            listener: OnReceiveContentListener?
        ) {
            super.setOnReceiveContentListener(mimeTypes, listener)
        }


        override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection {
            val ic: InputConnection = super.onCreateInputConnection(editorInfo)
            EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("image/gif"))

            val callback =
                InputConnectionCompat.OnCommitContentListener { inputContentInfo, _, _ ->
                    try {
                        inputContentInfo.requestPermission()
                    } catch (e: Exception) {
                        return@OnCommitContentListener false
                    }
                    true  // return true if succeeded
                }
            return InputConnectionCompat.createWrapper(ic, editorInfo, callback)
        }
    }
    editText
}) {}
mama
  • 2,046
  • 1
  • 7
  • 24

1 Answers1

4

callback is called each time a GIF is selected. You can get the URI from inputContentInfo:

val callback =
    InputConnectionCompat.OnCommitContentListener { inputContentInfo, _, _ ->
        try {
            inputContentInfo.requestPermission()
        } catch (e: Exception) {
            return@OnCommitContentListener false
        }
        gifUri = inputContentInfo.contentUri
        true  // return true if succeeded
    }

Perfectly, you should copy the file from this URI into your own storage and call inputContentInfo.releasePermission(), because contentUri will be freed at some point. More info can be found in the documentation.

You can display the content of this URI using Coil as showed in this answer. Note that you need to add the dependency io.coil-kt:coil-gif:$coil_version

A full working example:

var gifUri by remember { mutableStateOf<Uri?>(null) }
val context = LocalContext.current
Image(
    rememberImagePainter(
        gifUri,
        imageLoader = remember {
            ImageLoader(context).newBuilder()
                .componentRegistry {
                    if (SDK_INT >= 28) {
                        add(ImageDecoderDecoder(context))
                    } else {
                        add(GifDecoder())
                    }
                }.build()
        }
    ),
    contentDescription = null,
    modifier = Modifier.size(300.dp)
)

AndroidView(factory = { context ->
    val editText = @SuppressLint("AppCompatCustomView")
    object : EditText(context) {

        override fun setOnReceiveContentListener(
            mimeTypes: Array<out String>?,
            listener: OnReceiveContentListener?
        ) {
            super.setOnReceiveContentListener(mimeTypes, listener)
        }


        override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection {
            val ic: InputConnection = super.onCreateInputConnection(editorInfo)
            EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("image/gif"))

            val callback =
                InputConnectionCompat.OnCommitContentListener { inputContentInfo, _, _ ->
                    try {
                        inputContentInfo.requestPermission()
                    } catch (e: Exception) {
                        return@OnCommitContentListener false
                    }
                    gifUri = inputContentInfo.contentUri
                    true  // return true if succeeded
                }
            return InputConnectionCompat.createWrapper(ic, editorInfo, callback)
        }
    }
    editText
}) {}
Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • Thank you but I am getting an Error on this line `val input = inputContentInfo.contentUri.toFile().inputStream()` because `java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.samsung.android.honeyboard.provider/root/data/user/0/com.samsung.android.honeyboard/temp_content/com.samsung.android.icecone.gif/ff53e3cd-f276-4732-8232-02c47de5e1dc.gif` – mama Mar 07 '22 at 10:57
  • @mama I mean using URI directly, see updated answer. Not sure exactly how to copy it to local files. – Phil Dukhov Mar 07 '22 at 11:38
  • @mama this means that you are using a pre-release coil version, the API has been changed – Phil Dukhov Mar 07 '22 at 21:17
  • Is there any way to do it with an OutlinedTextField? – Jacob Ferrero May 27 '23 at 02:35
  • 1
    @JacobFerrero no, it’s not yet supported, otherwise [this issue](https://issuetracker.google.com/issues/198323023) would’ve been marked as complete – Phil Dukhov May 28 '23 at 00:52