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.
This commit is contained in:
2026-10-15 10:14:31 +02:00
parent 9a1aa9220b
commit d1bc61e717
@@ -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 -> {