From d1bc61e717635aee8c77b088aba5867a8ab52bdf Mon Sep 17 00:00:00 2001 From: Hadrian Burkhardt Date: Thu, 15 Oct 2026 10:14:31 +0200 Subject: [PATCH] Handle the Back button: close the overlay, then go up, then leave There was no BackHandler anywhere, so Back was left entirely to the system, which finishes the activity rather than navigating up a folder or closing a viewer. Verified on the phone: Back on the roots screen still exits, and Back inside a folder now goes up and stays in the app. The handler has to sit above the overlay branch, not after it: that branch returns early, so anything below it is never composed while a viewer or the editor is open, which is precisely when Back most needs an answer. The first attempt made that mistake and the device test caught it. --- .../filemanager/ui/browser/BrowserScreen.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/app/src/main/java/de/softwareapp_hb/filemanager/ui/browser/BrowserScreen.kt b/app/src/main/java/de/softwareapp_hb/filemanager/ui/browser/BrowserScreen.kt index c169c2e..78261d7 100644 --- a/app/src/main/java/de/softwareapp_hb/filemanager/ui/browser/BrowserScreen.kt +++ b/app/src/main/java/de/softwareapp_hb/filemanager/ui/browser/BrowserScreen.kt @@ -1,6 +1,7 @@ package de.softwareapp_hb.filemanager.ui.browser import android.content.Intent +import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Box @@ -133,6 +134,35 @@ fun BrowserScreen( state.pendingDeleteRequest?.let { deleteLauncher.launch(it) } } + // Back, in the order a person expects it: close what is on top, then go up a folder, + // and only then leave the app. `enabled` is false on the roots screen with nothing + // open, which lets the system finish the activity as usual. + // + // This has to sit *above* the overlay branch below: that branch returns early, so + // anything after it is never composed while a viewer or the editor is open — which is + // exactly when Back most needs an answer. + val closesOverlay = state.overlay != Overlay.None + val answersIncoming = state.receiving != null || state.providing || state.pendingDeliver != null + val canGoBack = closesOverlay || answersIncoming || searchOpen || + state.query.isNotEmpty() || state.isSelecting || !state.isRootView + + BackHandler(enabled = canGoBack) { + when { + closesOverlay -> viewModel.closeOverlay() + + answersIncoming -> viewModel.cancelIncoming() + + searchOpen || state.query.isNotEmpty() -> { + searchOpen = false + viewModel.setQuery("") + } + + state.isSelecting -> viewModel.clearSelection() + + else -> viewModel.navigateUp() + } + } + // A viewer, editor or the usage view takes over the whole screen. when (val overlay = state.overlay) { is Overlay.Image -> {