diff --git a/app/src/main/java/com/bitchat/android/MainActivity.kt b/app/src/main/java/com/bitchat/android/MainActivity.kt index aa336810..3d752291 100644 --- a/app/src/main/java/com/bitchat/android/MainActivity.kt +++ b/app/src/main/java/com/bitchat/android/MainActivity.kt @@ -313,15 +313,11 @@ class MainActivity : OrientationAwareActivity() { } OnboardingState.CHECKING, OnboardingState.INITIALIZING, OnboardingState.COMPLETE -> { - // Intercept Back only while the chat has navigation state to - // unwind. Staying disabled otherwise lets the dispatcher fall - // through to the system, which exits the app. val canHandleBack by chatViewModel.canHandleBack.collectAsState() + // Disabled rather than always-on so predictive back can preview + // the exit instead of the app claiming every gesture. BackHandler(enabled = canHandleBack) { - // enabled reaches this handler a dispatch and a recomposition - // after the state changes, so a second press can arrive while - // it is still true but there is no longer anything to unwind. - // Forward that press instead of swallowing it. + // enabled trails the state by a dispatch and a recomposition. if (!chatViewModel.handleBackPressed()) finish() } ChatScreen(viewModel = chatViewModel) diff --git a/app/src/main/java/com/bitchat/android/ui/ChatState.kt b/app/src/main/java/com/bitchat/android/ui/ChatState.kt index e0790894..16210894 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatState.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatState.kt @@ -170,10 +170,8 @@ class ChatState( initialValue = false ) - // True while some in-app navigation state is open for Back to unwind. - // Mirrors the branches of ChatViewModel.handleBackPressed: the back - // handler is enabled from this, the press is consumed by that, and the - // two drifting apart is what makes Back feel broken. + // Mirrors the branches of ChatViewModel.handleBackPressed. The back handler + // is enabled from this and the press consumed by that, so they must agree. val canHandleBack: StateFlow = combine( _showAppInfo, _showPasswordPrompt, diff --git a/app/src/test/kotlin/com/bitchat/android/ui/ChatStateBackNavigationTest.kt b/app/src/test/kotlin/com/bitchat/android/ui/ChatStateBackNavigationTest.kt index b0bacbd8..5c5c41de 100644 --- a/app/src/test/kotlin/com/bitchat/android/ui/ChatStateBackNavigationTest.kt +++ b/app/src/test/kotlin/com/bitchat/android/ui/ChatStateBackNavigationTest.kt @@ -12,10 +12,9 @@ import org.junit.Before import org.junit.Test /** - * `canHandleBack` tells the chat screen's back handler whether there is any - * in-app navigation state left to unwind. It has to agree with the branches of - * [ChatViewModel.handleBackPressed], because the handler is enabled from one - * and the press is consumed by the other. + * `canHandleBack` must agree with the branches of + * [ChatViewModel.handleBackPressed]: the back handler is enabled from one and + * the press consumed by the other. */ @OptIn(ExperimentalCoroutinesApi::class) class ChatStateBackNavigationTest { @@ -28,9 +27,7 @@ class ChatStateBackNavigationTest { fun setUp() { scope = TestScope(UnconfinedTestDispatcher()) state = ChatState(scope) - // canHandleBack is shared WhileSubscribed, so it only tracks its - // sources while something collects it. The composable does that in - // production; the test has to do it explicitly. + // Shared WhileSubscribed, so it only tracks its sources while collected. subscription = scope.launch { state.canHandleBack.collect { } } }