Skip to content

Commit

Permalink
Merge pull request #3533 from element-hq/feature/bma/fixCrashes
Browse files Browse the repository at this point in the history
Fix various crashes
  • Loading branch information
bmarty authored Sep 25, 2024
2 parents d0793be + 53fc2f3 commit 7f9e176
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,11 @@ class ElementCallActivity :

@RequiresApi(Build.VERSION_CODES.O)
override fun enterPipMode(): Boolean {
return enterPictureInPictureMode(getPictureInPictureParams())
return if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
enterPictureInPictureMode(getPictureInPictureParams())
} else {
false
}
}

@RequiresApi(Build.VERSION_CODES.O)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.voicemessages.VoiceMessageExcep
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runUpdatingState
import io.element.android.libraries.core.extensions.flatMap
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.ui.utils.time.formatShort
import io.element.android.services.analytics.api.AnalyticsService
Expand Down Expand Up @@ -126,8 +127,8 @@ class VoiceMessagePresenter @AssistedInject constructor(
it
},
) {
player.prepare().apply {
player.play()
player.prepare().flatMap {
runCatching { player.play() }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ fun Context.copyToClipboard(
* Shows notification settings for the current app.
* In android O will directly opens the notification settings, in lower version it will show the App settings
*/
fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResultLauncher<Intent>? = null) {
fun Context.startNotificationSettingsIntent(
activityResultLauncher: ActivityResultLauncher<Intent>? = null,
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
) {
val intent = Intent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
Expand All @@ -85,10 +88,14 @@ fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResu
intent.data = Uri.fromParts("package", packageName, null)
}

if (activityResultLauncher != null) {
activityResultLauncher.launch(intent)
} else {
startActivity(intent)
try {
if (activityResultLauncher != null) {
activityResultLauncher.launch(intent)
} else {
startActivity(intent)
}
} catch (activityNotFoundException: ActivityNotFoundException) {
toast(noActivityFoundMessage)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.RoomInfo
import org.matrix.rustcomponents.sdk.RoomInfoListener
Expand Down Expand Up @@ -104,10 +103,12 @@ class RustMatrixRoom(
override val roomId = RoomId(innerRoom.id())

override val roomInfoFlow: Flow<MatrixRoomInfo> = mxCallbackFlow {
launch {
val initial = innerRoom.roomInfo().let(matrixRoomInfoMapper::map)
channel.trySend(initial)
}
runCatching { innerRoom.roomInfo() }
.getOrNull()
?.let(matrixRoomInfoMapper::map)
?.let { initial ->
channel.trySend(initial)
}
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
override fun call(roomInfo: RoomInfo) {
channel.trySend(matrixRoomInfoMapper.map(roomInfo))
Expand All @@ -116,10 +117,8 @@ class RustMatrixRoom(
}

override val roomTypingMembersFlow: Flow<List<UserId>> = mxCallbackFlow {
launch {
val initial = emptyList<UserId>()
channel.trySend(initial)
}
val initial = emptyList<UserId>()
channel.trySend(initial)
innerRoom.subscribeToTypingNotifications(object : TypingNotificationsListener {
override fun call(typingUserIds: List<String>) {
channel.trySend(
Expand Down Expand Up @@ -625,9 +624,13 @@ class RustMatrixRoom(
innerRoom.sendCallNotificationIfNeeded()
}

override suspend fun setSendQueueEnabled(enabled: Boolean) = withContext(roomDispatcher) {
Timber.d("setSendQueuesEnabled: $enabled")
innerRoom.enableSendQueue(enabled)
override suspend fun setSendQueueEnabled(enabled: Boolean) {
withContext(roomDispatcher) {
Timber.d("setSendQueuesEnabled: $enabled")
runCatching {
innerRoom.enableSendQueue(enabled)
}
}
}

override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = runCatching {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ internal fun RoomListServiceInterface.stateFlow(): Flow<RoomListServiceState> =
trySendBlocking(state)
}
}
tryOrNull {
state(listener)
}
state(listener)
}.buffer(Channel.UNLIMITED)

internal fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncIndicator> =
Expand All @@ -109,13 +107,11 @@ internal fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncI
trySendBlocking(syncIndicator)
}
}
tryOrNull {
syncIndicator(
SYNC_INDICATOR_DELAY_BEFORE_SHOWING,
SYNC_INDICATOR_DELAY_BEFORE_HIDING,
listener,
)
}
syncIndicator(
SYNC_INDICATOR_DELAY_BEFORE_SHOWING,
SYNC_INDICATOR_DELAY_BEFORE_HIDING,
listener,
)
}.buffer(Channel.UNLIMITED)

internal fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

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

import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.trySendBlocking
Expand All @@ -24,7 +23,5 @@ fun SyncServiceInterface.stateFlow(): Flow<SyncServiceState> =
trySendBlocking(state)
}
}
tryOrNull {
state(listener)
}
state(listener)
}.buffer(Channel.UNLIMITED)
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import org.matrix.rustcomponents.sdk.TaskHandle

internal fun <T> mxCallbackFlow(block: suspend ProducerScope<T>.() -> TaskHandle?) =
internal fun <T> mxCallbackFlow(block: suspend ProducerScope<T>.() -> TaskHandle) =
callbackFlow {
val taskHandle: TaskHandle? = tryOrNull {
block(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

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

import android.content.ActivityNotFoundException
import androidx.activity.compose.ManagedActivityResultLauncher
import timber.log.Timber

/**
* Wrapper around [ManagedActivityResultLauncher] to be used with media/file pickers.
Expand All @@ -25,11 +27,19 @@ class ComposePickerLauncher<Input, Output>(
private val defaultRequest: Input,
) : PickerLauncher<Input, Output> {
override fun launch() {
managedLauncher.launch(defaultRequest)
try {
managedLauncher.launch(defaultRequest)
} catch (activityNotFoundException: ActivityNotFoundException) {
Timber.w(activityNotFoundException, "No activity found")
}
}

override fun launch(customInput: Input) {
managedLauncher.launch(customInput)
try {
managedLauncher.launch(customInput)
} catch (activityNotFoundException: ActivityNotFoundException) {
Timber.w(activityNotFoundException, "No activity found")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ package io.element.android.libraries.mediaviewer.api.local.pdf

import android.graphics.pdf.PdfRenderer
import android.os.ParcelFileDescriptor
import io.element.android.libraries.architecture.AsyncData
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -25,20 +28,30 @@ class PdfRendererManager(
) {
private val mutex = Mutex()
private var pdfRenderer: PdfRenderer? = null
private val mutablePdfPages = MutableStateFlow<List<PdfPage>>(emptyList())
val pdfPages: StateFlow<List<PdfPage>> = mutablePdfPages
private val mutablePdfPages = MutableStateFlow<AsyncData<ImmutableList<PdfPage>>>(AsyncData.Uninitialized)
val pdfPages: StateFlow<AsyncData<ImmutableList<PdfPage>>> = mutablePdfPages

fun open() {
coroutineScope.launch {
mutex.withLock {
withContext(Dispatchers.IO) {
pdfRenderer = PdfRenderer(parcelFileDescriptor).apply {
// Preload just 3 pages so we can render faster
val firstPages = loadPages(from = 0, to = 3)
mutablePdfPages.value = firstPages
val nextPages = loadPages(from = 3, to = pageCount)
mutablePdfPages.value = firstPages + nextPages
}
pdfRenderer = runCatching {
PdfRenderer(parcelFileDescriptor)
}.fold(
onSuccess = { pdfRenderer ->
pdfRenderer.apply {
// Preload just 3 pages so we can render faster
val firstPages = loadPages(from = 0, to = 3)
mutablePdfPages.value = AsyncData.Success(firstPages.toImmutableList())
val nextPages = loadPages(from = 3, to = pageCount)
mutablePdfPages.value = AsyncData.Success((firstPages + nextPages).toImmutableList())
}
},
onFailure = {
mutablePdfPages.value = AsyncData.Failure(it)
null
}
)
}
}
}
Expand All @@ -47,7 +60,7 @@ class PdfRendererManager(
fun close() {
coroutineScope.launch {
mutex.withLock {
mutablePdfPages.value.forEach { pdfPage ->
mutablePdfPages.value.dataOrNull()?.forEach { pdfPage ->
pdfPage.close()
}
pdfRenderer?.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.roundToPx
import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import me.saket.telephoto.zoomable.zoomable
import java.io.IOException

@Composable
fun PdfViewer(
Expand All @@ -59,14 +65,56 @@ fun PdfViewer(
}
val pdfPages = pdfViewerState.getPages()
PdfPagesView(
pdfPages = pdfPages.toImmutableList(),
pdfPages = pdfPages,
lazyListState = pdfViewerState.lazyListState,
)
}
}

@Composable
private fun PdfPagesView(
pdfPages: AsyncData<ImmutableList<PdfPage>>,
lazyListState: LazyListState,
modifier: Modifier = Modifier,
) {
when (pdfPages) {
is AsyncData.Uninitialized,
is AsyncData.Loading -> Unit
is AsyncData.Failure -> PdfPagesErrorView(
pdfPages.error,
modifier,
)
is AsyncData.Success -> PdfPagesContentView(
pdfPages = pdfPages.data,
lazyListState = lazyListState,
modifier = modifier
)
}
}

@Composable
private fun PdfPagesErrorView(
error: Throwable,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Text(
text = buildString {
append(stringResource(id = CommonStrings.error_unknown))
append("\n\n")
append(error.localizedMessage)
},
textAlign = TextAlign.Center,
style = ElementTheme.typography.fontBodyLgRegular,
)
}
}

@Composable
private fun PdfPagesContentView(
pdfPages: ImmutableList<PdfPage>,
lazyListState: LazyListState,
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -117,3 +165,11 @@ private fun PdfPageView(
}
}
}

@PreviewsDayNight
@Composable
internal fun PdfPagesErrorViewPreview() = ElementPreview {
PdfPagesErrorView(
error = IOException("file not in PDF format or corrupted"),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import io.element.android.libraries.architecture.AsyncData
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.CoroutineScope
import me.saket.telephoto.zoomable.ZoomableState
import me.saket.telephoto.zoomable.rememberZoomableState
Expand All @@ -35,10 +37,10 @@ class PdfViewerState(
private var pdfRendererManager by mutableStateOf<PdfRendererManager?>(null)

@Composable
fun getPages(): List<PdfPage> {
fun getPages(): AsyncData<ImmutableList<PdfPage>> {
return pdfRendererManager?.run {
pdfPages.collectAsState().value
} ?: emptyList()
} ?: AsyncData.Uninitialized
}

fun openForWidth(maxWidth: Int) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7f9e176

Please sign in to comment.