From 54705c07bdf501274db817c902dd650c408be3af Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Fri, 7 Oct 2022 23:40:37 +0300 Subject: [PATCH] android: Fixed bug with modal views (#1189) --- .../simplex/app/views/helpers/ModalView.kt | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt index ae184c1efe..7350b4339a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.graphics.Color import chat.simplex.app.TAG import chat.simplex.app.ui.theme.SettingsBackgroundLight import chat.simplex.app.ui.theme.isInDarkTheme -import kotlinx.coroutines.delay +import java.util.concurrent.atomic.AtomicBoolean @Composable fun ModalView( @@ -35,6 +35,7 @@ class ModalManager { private val modalViews = arrayListOf<(@Composable (close: () -> Unit) -> Unit)?>() private val modalCount = mutableStateOf(0) private val toRemove = mutableSetOf() + private var oldViewChanging = AtomicBoolean(false) fun showModal(settings: Boolean = false, content: @Composable () -> Unit) { showCustomModal { close -> @@ -50,6 +51,11 @@ class ModalManager { fun showCustomModal(modal: @Composable (close: () -> Unit) -> Unit) { Log.d(TAG, "ModalManager.showModal") + // Means, animation is in progress or not started yet. Do not wait until animation finishes, just remove all from screen. + // This is useful when invoking close() and ShowCustomModal one after another without delay. Otherwise, screen will hold prev view + if (toRemove.isNotEmpty()) { + runAtomically { toRemove.removeIf { elem -> modalViews.removeAt(elem); true } } + } modalViews.add(modal) modalCount.value = modalViews.size - toRemove.size } @@ -57,7 +63,7 @@ class ModalManager { fun closeModal() { if (modalViews.isNotEmpty()) { //modalViews.removeAt(modalViews.lastIndex) - toRemove.add(modalViews.lastIndex - toRemove.size) + runAtomically { toRemove.add(modalViews.lastIndex - toRemove.size) } } modalCount.value = modalViews.size - toRemove.size } @@ -81,11 +87,22 @@ class ModalManager { modalViews.getOrNull(it - 1)?.invoke(::closeModal) // This is needed because if we delete from modalViews immediately on request, animation will be bad if (toRemove.isNotEmpty() && it == modalCount.value && transition.currentState == EnterExitState.Visible && !transition.isRunning) { - toRemove.removeIf { elem -> modalViews.removeAt(elem); true } + runAtomically { toRemove.removeIf { elem -> modalViews.removeAt(elem); true } } } } } + /** + * Allows to modify a list without getting [ConcurrentModificationException] + * */ + private fun runAtomically(atomicBoolean: AtomicBoolean = oldViewChanging, block: () -> Unit) { + while (!atomicBoolean.compareAndSet(false, true)) { + Thread.sleep(10) + } + block() + atomicBoolean.set(false) + } + @OptIn(ExperimentalAnimationApi::class) private fun fromStartToEndTransition() = slideInHorizontally(