I'm trying to implement PdfViewer with Pdf Renderer Api and Jetpack Compose. Here is my code:
@Composable
fun PdfViewer(uri: Uri) {
val context = LocalContext.current
val rendererScope = rememberCoroutineScope()
val mutex = remember { Mutex() }
val renderer by produceState<PdfRenderer?>(null, uri) {
rendererScope.launch(Dispatchers.IO) {
val fileDescriptor =
context.contentResolver.openFileDescriptor(uri, "r") ?: return@launch
value = PdfRenderer(fileDescriptor)
}
awaitDispose {
val currentRenderer = value
rendererScope.launch(Dispatchers.IO) {
mutex.withLock { currentRenderer?.close() }
}
}
}
val imageLoadingScope = rememberCoroutineScope()
BoxWithConstraints(
Modifier
.fillMaxWidth()
.background(Color.Gray)
) {
val width = with(LocalDensity.current) { maxWidth.toPx() }.toInt()
val height = (width * sqrt(2f)).toInt()
val pageCount by remember(renderer) { derivedStateOf { renderer?.pageCount ?: 0 } }
LazyColumn(
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(
count = pageCount,
key = { index -> "$uri:$index" }
) { index ->
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
if (bitmap == null) {
DisposableEffect(uri, index) {
val job = imageLoadingScope.launch(Dispatchers.IO) {
val destinationBitmap =
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
mutex.withLock {
if (!coroutineContext.isActive) return@launch
try {
renderer?.let {
it.openPage(index).use { page ->
page.render(
destinationBitmap,
null,
null,
PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY
)
}
}
} catch (e: Exception) {
return@launch
}
}
bitmap = destinationBitmap
}
onDispose { job.cancel() }
}
Box(
modifier = Modifier
.background(Color.White)
.aspectRatio(1f / sqrt(2f))
.fillMaxWidth()
)
} else {
bitmap?.let {
Image(
modifier = Modifier
.background(Color.White)
.aspectRatio(1f / sqrt(2f))
.fillMaxWidth(),
contentScale = ContentScale.Fit,
bitmap = it.asImageBitmap(),
contentDescription = "page with index = $index"
)
}
}
}
}
}
}
When I open the first pdf document everything works fine, however if I call the fun PdfViewer(uri: Uri)
again with a new Uri
to open another pdf document, the first page doesn't load because I get an exception when i try to open the page (openPage
):
java.lang.IllegalStateException: Already closed android.graphics.pdf.PdfRenderer.throwIfClosed(PdfRenderer.java:273)
Please tell me how can I fix this?
P.S. If I comment out this part of the code, then everything works correctly, but I'm not sure if this is correct.
awaitDispose {
val currentRenderer = value
rendererScope.launch(Dispatchers.IO) {
mutex.withLock { currentRenderer?.close() }
}
}