Skip to content

Commit 7f9e176

Browse files
authored
Merge pull request #3533 from element-hq/feature/bma/fixCrashes
Fix various crashes
2 parents d0793be + 53fc2f3 commit 7f9e176

File tree

13 files changed

+146
-51
lines changed

13 files changed

+146
-51
lines changed

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,11 @@ class ElementCallActivity :
281281

282282
@RequiresApi(Build.VERSION_CODES.O)
283283
override fun enterPipMode(): Boolean {
284-
return enterPictureInPictureMode(getPictureInPictureParams())
284+
return if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
285+
enterPictureInPictureMode(getPictureInPictureParams())
286+
} else {
287+
false
288+
}
285289
}
286290

287291
@RequiresApi(Build.VERSION_CODES.O)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.voicemessages.VoiceMessageExcep
2727
import io.element.android.libraries.architecture.AsyncData
2828
import io.element.android.libraries.architecture.Presenter
2929
import io.element.android.libraries.architecture.runUpdatingState
30+
import io.element.android.libraries.core.extensions.flatMap
3031
import io.element.android.libraries.di.RoomScope
3132
import io.element.android.libraries.ui.utils.time.formatShort
3233
import io.element.android.services.analytics.api.AnalyticsService
@@ -126,8 +127,8 @@ class VoiceMessagePresenter @AssistedInject constructor(
126127
it
127128
},
128129
) {
129-
player.prepare().apply {
130-
player.play()
130+
player.prepare().flatMap {
131+
runCatching { player.play() }
131132
}
132133
}
133134
}

libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ fun Context.copyToClipboard(
7171
* Shows notification settings for the current app.
7272
* In android O will directly opens the notification settings, in lower version it will show the App settings
7373
*/
74-
fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResultLauncher<Intent>? = null) {
74+
fun Context.startNotificationSettingsIntent(
75+
activityResultLauncher: ActivityResultLauncher<Intent>? = null,
76+
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
77+
) {
7578
val intent = Intent()
7679
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
7780
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
@@ -85,10 +88,14 @@ fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResu
8588
intent.data = Uri.fromParts("package", packageName, null)
8689
}
8790

88-
if (activityResultLauncher != null) {
89-
activityResultLauncher.launch(intent)
90-
} else {
91-
startActivity(intent)
91+
try {
92+
if (activityResultLauncher != null) {
93+
activityResultLauncher.launch(intent)
94+
} else {
95+
startActivity(intent)
96+
}
97+
} catch (activityNotFoundException: ActivityNotFoundException) {
98+
toast(noActivityFoundMessage)
9299
}
93100
}
94101

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ import kotlinx.coroutines.flow.launchIn
6868
import kotlinx.coroutines.flow.map
6969
import kotlinx.coroutines.flow.onEach
7070
import kotlinx.coroutines.flow.onStart
71-
import kotlinx.coroutines.launch
7271
import kotlinx.coroutines.withContext
7372
import org.matrix.rustcomponents.sdk.RoomInfo
7473
import org.matrix.rustcomponents.sdk.RoomInfoListener
@@ -104,10 +103,12 @@ class RustMatrixRoom(
104103
override val roomId = RoomId(innerRoom.id())
105104

106105
override val roomInfoFlow: Flow<MatrixRoomInfo> = mxCallbackFlow {
107-
launch {
108-
val initial = innerRoom.roomInfo().let(matrixRoomInfoMapper::map)
109-
channel.trySend(initial)
110-
}
106+
runCatching { innerRoom.roomInfo() }
107+
.getOrNull()
108+
?.let(matrixRoomInfoMapper::map)
109+
?.let { initial ->
110+
channel.trySend(initial)
111+
}
111112
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
112113
override fun call(roomInfo: RoomInfo) {
113114
channel.trySend(matrixRoomInfoMapper.map(roomInfo))
@@ -116,10 +117,8 @@ class RustMatrixRoom(
116117
}
117118

118119
override val roomTypingMembersFlow: Flow<List<UserId>> = mxCallbackFlow {
119-
launch {
120-
val initial = emptyList<UserId>()
121-
channel.trySend(initial)
122-
}
120+
val initial = emptyList<UserId>()
121+
channel.trySend(initial)
123122
innerRoom.subscribeToTypingNotifications(object : TypingNotificationsListener {
124123
override fun call(typingUserIds: List<String>) {
125124
channel.trySend(
@@ -625,9 +624,13 @@ class RustMatrixRoom(
625624
innerRoom.sendCallNotificationIfNeeded()
626625
}
627626

628-
override suspend fun setSendQueueEnabled(enabled: Boolean) = withContext(roomDispatcher) {
629-
Timber.d("setSendQueuesEnabled: $enabled")
630-
innerRoom.enableSendQueue(enabled)
627+
override suspend fun setSendQueueEnabled(enabled: Boolean) {
628+
withContext(roomDispatcher) {
629+
Timber.d("setSendQueuesEnabled: $enabled")
630+
runCatching {
631+
innerRoom.enableSendQueue(enabled)
632+
}
633+
}
631634
}
632635

633636
override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = runCatching {

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ internal fun RoomListServiceInterface.stateFlow(): Flow<RoomListServiceState> =
9797
trySendBlocking(state)
9898
}
9999
}
100-
tryOrNull {
101-
state(listener)
102-
}
100+
state(listener)
103101
}.buffer(Channel.UNLIMITED)
104102

105103
internal fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncIndicator> =
@@ -109,13 +107,11 @@ internal fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncI
109107
trySendBlocking(syncIndicator)
110108
}
111109
}
112-
tryOrNull {
113-
syncIndicator(
114-
SYNC_INDICATOR_DELAY_BEFORE_SHOWING,
115-
SYNC_INDICATOR_DELAY_BEFORE_HIDING,
116-
listener,
117-
)
118-
}
110+
syncIndicator(
111+
SYNC_INDICATOR_DELAY_BEFORE_SHOWING,
112+
SYNC_INDICATOR_DELAY_BEFORE_HIDING,
113+
listener,
114+
)
119115
}.buffer(Channel.UNLIMITED)
120116

121117
internal fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? {

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
package io.element.android.libraries.matrix.impl.sync
99

10-
import io.element.android.libraries.core.data.tryOrNull
1110
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
1211
import kotlinx.coroutines.channels.Channel
1312
import kotlinx.coroutines.channels.trySendBlocking
@@ -24,7 +23,5 @@ fun SyncServiceInterface.stateFlow(): Flow<SyncServiceState> =
2423
trySendBlocking(state)
2524
}
2625
}
27-
tryOrNull {
28-
state(listener)
29-
}
26+
state(listener)
3027
}.buffer(Channel.UNLIMITED)

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import kotlinx.coroutines.channels.awaitClose
1313
import kotlinx.coroutines.flow.callbackFlow
1414
import org.matrix.rustcomponents.sdk.TaskHandle
1515

16-
internal fun <T> mxCallbackFlow(block: suspend ProducerScope<T>.() -> TaskHandle?) =
16+
internal fun <T> mxCallbackFlow(block: suspend ProducerScope<T>.() -> TaskHandle) =
1717
callbackFlow {
1818
val taskHandle: TaskHandle? = tryOrNull {
1919
block(this)

libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerLauncher.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
package io.element.android.libraries.mediapickers.api
99

10+
import android.content.ActivityNotFoundException
1011
import androidx.activity.compose.ManagedActivityResultLauncher
12+
import timber.log.Timber
1113

1214
/**
1315
* Wrapper around [ManagedActivityResultLauncher] to be used with media/file pickers.
@@ -25,11 +27,19 @@ class ComposePickerLauncher<Input, Output>(
2527
private val defaultRequest: Input,
2628
) : PickerLauncher<Input, Output> {
2729
override fun launch() {
28-
managedLauncher.launch(defaultRequest)
30+
try {
31+
managedLauncher.launch(defaultRequest)
32+
} catch (activityNotFoundException: ActivityNotFoundException) {
33+
Timber.w(activityNotFoundException, "No activity found")
34+
}
2935
}
3036

3137
override fun launch(customInput: Input) {
32-
managedLauncher.launch(customInput)
38+
try {
39+
managedLauncher.launch(customInput)
40+
} catch (activityNotFoundException: ActivityNotFoundException) {
41+
Timber.w(activityNotFoundException, "No activity found")
42+
}
3343
}
3444
}
3545

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

Lines changed: 23 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,30 @@ 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+
pdfRenderer = runCatching {
39+
PdfRenderer(parcelFileDescriptor)
40+
}.fold(
41+
onSuccess = { pdfRenderer ->
42+
pdfRenderer.apply {
43+
// Preload just 3 pages so we can render faster
44+
val firstPages = loadPages(from = 0, to = 3)
45+
mutablePdfPages.value = AsyncData.Success(firstPages.toImmutableList())
46+
val nextPages = loadPages(from = 3, to = pageCount)
47+
mutablePdfPages.value = AsyncData.Success((firstPages + nextPages).toImmutableList())
48+
}
49+
},
50+
onFailure = {
51+
mutablePdfPages.value = AsyncData.Failure(it)
52+
null
53+
}
54+
)
4255
}
4356
}
4457
}
@@ -47,7 +60,7 @@ class PdfRendererManager(
4760
fun close() {
4861
coroutineScope.launch {
4962
mutex.withLock {
50-
mutablePdfPages.value.forEach { pdfPage ->
63+
mutablePdfPages.value.dataOrNull()?.forEach { pdfPage ->
5164
pdfPage.close()
5265
}
5366
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+
private 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+
}

0 commit comments

Comments
 (0)