Skip to content

Commit 92834e8

Browse files
committed
1 parent 900074e commit 92834e8

File tree

3 files changed

+83
-14
lines changed

3 files changed

+83
-14
lines changed

libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfRendererManager.kt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ package io.element.android.libraries.mediaviewer.api.local.pdf
99

1010
import android.graphics.pdf.PdfRenderer
1111
import android.os.ParcelFileDescriptor
12+
import io.element.android.libraries.architecture.AsyncData
13+
import kotlinx.collections.immutable.ImmutableList
14+
import kotlinx.collections.immutable.toImmutableList
1215
import kotlinx.coroutines.CoroutineScope
1316
import kotlinx.coroutines.Dispatchers
1417
import kotlinx.coroutines.flow.MutableStateFlow
@@ -25,20 +28,28 @@ class PdfRendererManager(
2528
) {
2629
private val mutex = Mutex()
2730
private var pdfRenderer: PdfRenderer? = null
28-
private val mutablePdfPages = MutableStateFlow<List<PdfPage>>(emptyList())
29-
val pdfPages: StateFlow<List<PdfPage>> = mutablePdfPages
31+
private val mutablePdfPages = MutableStateFlow<AsyncData<ImmutableList<PdfPage>>>(AsyncData.Uninitialized)
32+
val pdfPages: StateFlow<AsyncData<ImmutableList<PdfPage>>> = mutablePdfPages
3033

3134
fun open() {
3235
coroutineScope.launch {
3336
mutex.withLock {
3437
withContext(Dispatchers.IO) {
35-
pdfRenderer = PdfRenderer(parcelFileDescriptor).apply {
36-
// Preload just 3 pages so we can render faster
37-
val firstPages = loadPages(from = 0, to = 3)
38-
mutablePdfPages.value = firstPages
39-
val nextPages = loadPages(from = 3, to = pageCount)
40-
mutablePdfPages.value = firstPages + nextPages
41-
}
38+
runCatching {
39+
PdfRenderer(parcelFileDescriptor)
40+
}.fold(
41+
onSuccess = { pdfRenderer ->
42+
this@PdfRendererManager.pdfRenderer = pdfRenderer
43+
// Preload just 3 pages so we can render faster
44+
val firstPages = pdfRenderer.loadPages(from = 0, to = 3)
45+
mutablePdfPages.value = AsyncData.Success(firstPages.toImmutableList())
46+
val nextPages = pdfRenderer.loadPages(from = 3, to = pdfRenderer.pageCount)
47+
mutablePdfPages.value = AsyncData.Success((firstPages + nextPages).toImmutableList())
48+
},
49+
onFailure = {
50+
mutablePdfPages.value = AsyncData.Failure(it)
51+
}
52+
)
4253
}
4354
}
4455
}
@@ -47,7 +58,7 @@ class PdfRendererManager(
4758
fun close() {
4859
coroutineScope.launch {
4960
mutex.withLock {
50-
mutablePdfPages.value.forEach { pdfPage ->
61+
mutablePdfPages.value.dataOrNull()?.forEach { pdfPage ->
5162
pdfPage.close()
5263
}
5364
pdfRenderer?.close()

libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewer.kt

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@ import androidx.compose.ui.graphics.Color
2828
import androidx.compose.ui.graphics.asImageBitmap
2929
import androidx.compose.ui.layout.ContentScale
3030
import androidx.compose.ui.res.stringResource
31+
import androidx.compose.ui.text.style.TextAlign
3132
import androidx.compose.ui.unit.dp
33+
import io.element.android.compound.theme.ElementTheme
34+
import io.element.android.libraries.architecture.AsyncData
35+
import io.element.android.libraries.designsystem.preview.ElementPreview
36+
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
3237
import io.element.android.libraries.designsystem.text.roundToPx
3338
import io.element.android.libraries.designsystem.text.toDp
39+
import io.element.android.libraries.designsystem.theme.components.Text
3440
import io.element.android.libraries.ui.strings.CommonStrings
3541
import kotlinx.collections.immutable.ImmutableList
36-
import kotlinx.collections.immutable.toImmutableList
3742
import me.saket.telephoto.zoomable.zoomable
43+
import java.io.IOException
3844

3945
@Composable
4046
fun PdfViewer(
@@ -59,14 +65,56 @@ fun PdfViewer(
5965
}
6066
val pdfPages = pdfViewerState.getPages()
6167
PdfPagesView(
62-
pdfPages = pdfPages.toImmutableList(),
68+
pdfPages = pdfPages,
6369
lazyListState = pdfViewerState.lazyListState,
6470
)
6571
}
6672
}
6773

6874
@Composable
6975
private fun PdfPagesView(
76+
pdfPages: AsyncData<ImmutableList<PdfPage>>,
77+
lazyListState: LazyListState,
78+
modifier: Modifier = Modifier,
79+
) {
80+
when (pdfPages) {
81+
is AsyncData.Uninitialized,
82+
is AsyncData.Loading -> Unit
83+
is AsyncData.Failure -> PdfPagesErrorView(
84+
pdfPages.error,
85+
modifier,
86+
)
87+
is AsyncData.Success -> PdfPagesContentView(
88+
pdfPages = pdfPages.data,
89+
lazyListState = lazyListState,
90+
modifier = modifier
91+
)
92+
}
93+
}
94+
95+
@Composable
96+
fun PdfPagesErrorView(
97+
error: Throwable,
98+
modifier: Modifier = Modifier,
99+
) {
100+
Box(
101+
modifier = modifier.fillMaxSize(),
102+
contentAlignment = Alignment.Center,
103+
) {
104+
Text(
105+
text = buildString {
106+
append(stringResource(id = CommonStrings.error_unknown))
107+
append("\n\n")
108+
append(error.localizedMessage)
109+
},
110+
textAlign = TextAlign.Center,
111+
style = ElementTheme.typography.fontBodyLgRegular,
112+
)
113+
}
114+
}
115+
116+
@Composable
117+
private fun PdfPagesContentView(
70118
pdfPages: ImmutableList<PdfPage>,
71119
lazyListState: LazyListState,
72120
modifier: Modifier = Modifier,
@@ -117,3 +165,11 @@ private fun PdfPageView(
117165
}
118166
}
119167
}
168+
169+
@PreviewsDayNight
170+
@Composable
171+
internal fun PdfPagesErrorViewPreview() = ElementPreview {
172+
PdfPagesErrorView(
173+
error = IOException("file not in PDF format or corrupted"),
174+
)
175+
}

libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/pdf/PdfViewerState.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import androidx.compose.runtime.remember
1919
import androidx.compose.runtime.rememberCoroutineScope
2020
import androidx.compose.runtime.setValue
2121
import androidx.compose.ui.platform.LocalContext
22+
import io.element.android.libraries.architecture.AsyncData
23+
import kotlinx.collections.immutable.ImmutableList
2224
import kotlinx.coroutines.CoroutineScope
2325
import me.saket.telephoto.zoomable.ZoomableState
2426
import me.saket.telephoto.zoomable.rememberZoomableState
@@ -35,10 +37,10 @@ class PdfViewerState(
3537
private var pdfRendererManager by mutableStateOf<PdfRendererManager?>(null)
3638

3739
@Composable
38-
fun getPages(): List<PdfPage> {
40+
fun getPages(): AsyncData<ImmutableList<PdfPage>> {
3941
return pdfRendererManager?.run {
4042
pdfPages.collectAsState().value
41-
} ?: emptyList()
43+
} ?: AsyncData.Uninitialized
4244
}
4345

4446
fun openForWidth(maxWidth: Int) {

0 commit comments

Comments
 (0)