Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rememberAsyncImagePainter renders nothing when using dragAndDropSource #2150

Open
vjxDev opened this issue Feb 29, 2024 · 8 comments
Open

rememberAsyncImagePainter renders nothing when using dragAndDropSource #2150

vjxDev opened this issue Feb 29, 2024 · 8 comments
Labels
help wanted Issues that are up for grabs + are good candidates for community PRs

Comments

@vjxDev
Copy link

vjxDev commented Feb 29, 2024

Describe the bug
The image is simply rendering nothing. There is nothing in the logs. When I remove the dragAndDropSource it works fine.

To Reproduce

                Box(
                    modifier = Modifier
                        .aspectRatio(1f)
                        .dragAndDropSource {
                            detectTapGestures (
                                onLongPress = {
                                    startTransfer(
                                        DragAndDropTransferData(
                                            clipData = ClipData.newPlainText("text/","${photo.id}"),
                                            flags = View.DRAG_FLAG_GLOBAL,
                                        )
                                    )
                                }
                            )
                        }
                ) {
                    Image(
                        painter = rememberAsyncImagePainter(photo.url),
                        contentDescription = photo.description,
                        contentScale = ContentScale.Crop,
                        modifier = Modifier.fillMaxSize()
                    )
                    Text(text = "${photo.id}")
                }

Logs/Screenshots
Screenshot_20240229-013851 Screenshot_20240229-013928

Version
I am using a google pixel 6, android 14.
coil-compose = "2.6.0"
activity-compose = "1.7.0"

@colinrtwhite
Copy link
Member

If you use AsyncImage does it fix it? rememberAsyncImagePainter isn't easily able to determine the output bounds which can cause issues.

@colinrtwhite colinrtwhite added the help wanted Issues that are up for grabs + are good candidates for community PRs label Feb 29, 2024
@vjxDev
Copy link
Author

vjxDev commented Feb 29, 2024

I just tried it with AsyncImage . Its blank until I long press on a box, only then does it render the images. I only now noticed it does the same thing with rememberAsyncImagePainter.

It seams like it only renders the images when a recomposition happens.

screen-20240229-023455.mp4

@colinrtwhite
Copy link
Member

Interesting - if it also happens with AsyncImage then it might not be related to the constraints. Just to confirm if you set a fixed size on the image request (e.g. size(Size(500, 500))) does it work-around the issue?

@vjxDev
Copy link
Author

vjxDev commented Mar 1, 2024

Its still blank after adding modifier size until the 1st state update.

AsyncImage(
  model = photo.url,
  contentDescription = photo.description,
  contentScale = ContentScale.Crop,
   modifier = Modifier.size(20.dp, 20.dp),
)

The dragAndDropSource and dragAndDropTarget are under ExperimentalFoundationApi they might be the problem.

@TinyHai
Copy link

TinyHai commented Apr 7, 2024

I reproduce this issue by applying Modifier#drawWithCache to AsyncImage

Modifier.drawWithCache {
    val picture = Picture()
    onDrawWithContent {
        val canvas = Canvas(picture.beginRecording(size.width.toInt(), size.height.toInt()))
        draw(this, layoutDirection, canvas, size) {
            this@onDrawWithContent.drawContent()
        }
        picture.endRecording()
        drawIntoCanvas { it.nativeCanvas.drawPicture(picture) }
    }
}

the code is copy from androidx.compose.foundation.draganddrop.CacheDrawScopeDragShadowCallback.

I suppose it cause this issue, but I don't know why.

Hope it's useful for you.

@TinyHai
Copy link

TinyHai commented Apr 8, 2024

I reproduce this issue by applying Modifier#drawWithCache to AsyncImage

Modifier.drawWithCache {
    val picture = Picture()
    onDrawWithContent {
        val canvas = Canvas(picture.beginRecording(size.width.toInt(), size.height.toInt()))
        draw(this, layoutDirection, canvas, size) {
            this@onDrawWithContent.drawContent()
        }
        picture.endRecording()
        drawIntoCanvas { it.nativeCanvas.drawPicture(picture) }
    }
}

the code is copy from androidx.compose.foundation.draganddrop.CacheDrawScopeDragShadowCallback.

I suppose it cause this issue, but I don't know why.

Hope it's useful for you.

In dragAndDropSource, it uses Picture to capture a snapshot by calling drawContent, but it doesn't recall it when inner content changed. It seems like drawing phase is localized.

As a result, it will draw the empty snapshot until next recompose comes.

@TinyHai
Copy link

TinyHai commented Apr 8, 2024

I found a similar issue on issue tracker

@Wardroben
Copy link

Wardroben commented Sep 11, 2024

Noticed that the problem is observed with some images. Some images consistently fail to load, others on the contrary load.
I'm writing an app that allows you to select images from the web (google images) and images from your device. All images taken from the network and saved as follows are displayed stably:

val request = ImageRequest.Builder(context = context)
                        .data(url)
                        .crossfade(true)
                        .build()
                    AsyncImage(
                        //some parameters
                        modifier = Modifier
                            //some modifiers
                            .clickable {
                                scope.launch {
                                    val result = (loader.execute(request) as SuccessResult).drawable
                                    val bitmap = (result as BitmapDrawable).bitmap
                                    onImageAdd(index, bitmap ?: return@launch)
                                }
                            }

The bitmap is then saved to disk via FileOutputStream and I get a Uri on it:

private fun saveCompressedImage(bitmap: Bitmap): Uri {
        val fileName = getRandomName()
        checkAndCreateOutputDir()

        val imageFile = File(outputDirectory, fileName)
        try {
            FileOutputStream(imageFile).use { outputStream ->
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                    bitmap.compress(CompressFormat.WEBP_LOSSY, 50, outputStream)
                } else {
                    bitmap.compress(CompressFormat.JPEG, 65, outputStream)
                }
            }
        } catch (e: IOException) {
            Log.e(TAG, e.message.toString())
            throw IOException(e.message)
        }
        return imageFile.toUri()
    }

Images from the device memory are saved through the same function with FileOutputStream, only I read bitmap through content resolver beforehand.

private fun readImage(uri: Uri): Bitmap {
        try {
            appContext.contentResolver.openInputStream(uri).use { inputStream ->
                return BitmapFactory.decodeStream(inputStream)
            }
        } //catches

I get Uri on these images via rememberLauncherForActivityResult with PickMultipleVisualMedia contract.

For example, I'm having trouble with this image saved from device memory:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Issues that are up for grabs + are good candidates for community PRs
Projects
None yet
Development

No branches or pull requests

4 participants