Giphy / giphy-android-sdk

Home of the GIPHY SDK Android example app, along with Android SDK documentation, issue tracking, & release notes.
https://developers.giphy.com/
Mozilla Public License 2.0
94 stars 37 forks source link

Gif is not loaded even after `onImageSet` callback #219

Closed ygag-gopikrishnan closed 5 months ago

ygag-gopikrishnan commented 7 months ago

🐛 Bug Report

To Reproduce

(Write your steps here:)

@Composable
fun GifPreview(
    modifier: Modifier = Modifier,
    gifId: String,
) {
    var isLoading by remember(gifId) {
        mutableStateOf(true)
    }

    AndroidView(
        modifier = Modifier
            .then(modifier)
            .shimmer(isLoading),
        factory = { context ->
            GPHMediaView(context)
        },
        update = { view ->
            with(view) {
                setMediaWithId(
                    gifId,
                    RenditionType.original,
                )
                setOnClickListener {
                    NoOperation
                }
                gifCallback = object : GifView.GifCallback {
                    override fun onFailure(throwable: Throwable?) {
                        isLoading = false
                    }

                    override fun onImageSet(imageInfo: ImageInfo?, anim: Animatable?, loopDuration: Long, loopCount: Int) {
                        isLoading = false
                    }
                }
                setOnLongClickListener {
                    false
                }
            }
        }
    )
}

Expected behavior

Actual Behavior

(Write what happened. Add screenshots, if applicable.)

Your Environment

Reproducible Demo

ALexanderLonsky commented 7 months ago

Hey @ygag-gopikrishnan, The GifView.GifCallback is not documented and is mainly used for internal purposes. It's called in phases, considering that you're requesting the original rendition, the gif view will load multiple renditions in several steps starting from the lowest one. For example, if you set RenditionType.fixedWidth, the delay should be reduced.

ygag-gopikrishnan commented 7 months ago

Is there any way I can get to know when the GIF is loaded completely? @ALexanderLonsky

ALexanderLonsky commented 7 months ago

As an option, you can compare the width and height of the required rendition:

setMediaWithId(
                        media.id,
                        RenditionType.original,
                        null,
                        { result, e ->
                            val media = result?.data?.images?.original
                            val requiredWidth = media?.width
                            val requiredHeight = media?.height
                        }
                    )
...
override fun onImageSet(imageInfo: ImageInfo?, anim: Animatable?, loopDuration: Long, loopCount: Int) {
                            val loadedWidth = imageInfo?.width
                            val loadedHeight = imageInfo?.height
                        }

The setMediaWithId callback is called immediately after the media object has loaded. Then it starts loading its content( renditions)

ygag-gopikrishnan commented 7 months ago

result, e -> val media = result?.data?.images?.original val requiredWidth = media?.width val requiredHeight = media?.height

Are you suggesting something like this?

@Composable
fun GifPreview(
    modifier: Modifier = Modifier,
    gifId: String,
    onGifLoaded: (
        durationInMillis: Long,
    ) -> Unit = {},
) {
    var isLoading by remember(gifId) {
        mutableStateOf(true)
    }

    var requiredWidth by remember {
        mutableStateOf(0)
    }
    var requiredHeight by remember {
        mutableStateOf(0)
    }

    AndroidView(
        modifier = Modifier
            .then(modifier)
            .shimmer(isLoading),
        factory = { context ->
            GPHMediaView(context)
        }
    ) { view ->
        with(view) {
            setMediaWithId(
                gifId,
                RenditionType.original,
                null
            ) { result, _ ->
                val media = result?.data?.images?.original
                requiredWidth = media?.width ?: 0
                requiredHeight = media?.height ?: 0
            }
            setOnClickListener {
                NoOperation
            }
            gifCallback = object : GifView.GifCallback {
                override fun onFailure(throwable: Throwable?) {
                    isLoading = false
                }

                override fun onImageSet(imageInfo: ImageInfo?, anim: Animatable?, loopDuration: Long, loopCount: Int) {
                    val loadedWidth = imageInfo?.width
                    val loadedHeight = imageInfo?.height
                    if (loadedWidth == requiredWidth
                        && loadedHeight == requiredHeight
                    ) {
                        isLoading = false
                        onGifLoaded(
                            loopDuration
                        )
                    }
                }
            }
            setOnLongClickListener {
                false
            }
        }
    }
}

@ALexanderLonsky

ALexanderLonsky commented 7 months ago

Yes, something like this. Please keep in mind that these are undocumented APIs and may be subject to change in the future.

ygag-gopikrishnan commented 7 months ago

So, you are saying there is no officially documented way to know when the GIF has completed loading?

ALexanderLonsky commented 7 months ago

Yes, as I already mentioned, the GifView.GifCallback is not documented and is primarily used for internal purposes. As an alternative, you can create your own custom view to display a gif using Fresco SimpleDraweeView with BaseControllerListener, which includes the onFinalImageSet method.

ygag-gopikrishnan commented 7 months ago

Yes, as I already mentioned, the GifView.GifCallback is not documented and is primarily used for internal purposes. As an alternative, you can create your own custom view to display a gif using Fresco SimpleDraweeView with BaseControllerListener, which includes the onFinalImageSet method.

Is there any material that I can refer to for the above-mentioned implementation? I am getting GIPHY gif key to get the Gif @ALexanderLonsky

ALexanderLonsky commented 7 months ago

If by gif key you are referring to gif id, then you can retrieve the full media content by following this: GPHCore.gifById(id)

You access a desired rendition like this: result?.data?.images.fixedWidth.gifUrl

Here you can read more about the renditions.

We also have a handy function to obtain the specific rendition: result?.data?.imageWithRenditionType(RenditionType.fixedWidth).gifUrl

Finally, you can use the gif url with Fresco, like this

Or use it with any other solution you prefer.

ygag-gopikrishnan commented 7 months ago

Thanks for the detailed explanation. One last question.

The GPHMediaView used to give the duration of Gif. override fun onImageSet(imageInfo: ImageInfo?, anim: Animatable?, loopDuration: Long, loopCount: Int)

Is there any way I can retrieve the same from Giphy?

FYI, I used Coil to load the Gif using URL.

@ALexanderLonsky

ALexanderLonsky commented 7 months ago

loopDuration comes from the Fresco part. The GIPHY SDK itself does not provide this information, with the exception for clips.