diff --git a/app/src/main/java/de/jeanlucmakiola/agendula/MainActivity.kt b/app/src/main/java/de/jeanlucmakiola/agendula/MainActivity.kt index f6386e4..9a47ca4 100644 --- a/app/src/main/java/de/jeanlucmakiola/agendula/MainActivity.kt +++ b/app/src/main/java/de/jeanlucmakiola/agendula/MainActivity.kt @@ -104,6 +104,7 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), navRequest = navRequest, onNavRequestConsumed = { navRequest = null }, + onReturnHome = ::finish, ) pendingCrashReport?.let { report -> CrashReportDialog( @@ -152,6 +153,7 @@ class MainActivity : ComponentActivity() { private const val EXTRA_FILTER_ID = "de.jeanlucmakiola.agendula.extra.FILTER_ID" private const val EXTRA_SMART_LIST = "de.jeanlucmakiola.agendula.extra.SMART_LIST" private const val EXTRA_LIST_ID = "de.jeanlucmakiola.agendula.extra.LIST_ID" + private const val EXTRA_RETURN_HOME = "de.jeanlucmakiola.agendula.extra.RETURN_HOME" private const val EXTRA_PRESET_LIST_ID = "de.jeanlucmakiola.agendula.extra.PRESET_LIST_ID" private const val SHARED_TITLE_LIMIT = 500 private const val NO_OCCURRENCE = -1L @@ -160,10 +162,16 @@ class MainActivity : ComponentActivity() { * Opens a task's detail (reminder taps). [occurrenceStart] (epoch millis) * picks the occurrence of a recurring task. */ - fun taskIntent(context: Context, taskId: Long, occurrenceStart: Long? = null): Intent = + fun taskIntent( + context: Context, + taskId: Long, + occurrenceStart: Long? = null, + returnHome: Boolean = false, + ): Intent = Intent(context, MainActivity::class.java).apply { putExtra(EXTRA_TASK_ID, taskId) putExtra(EXTRA_OCCURRENCE_START, occurrenceStart ?: NO_OCCURRENCE) + putExtra(EXTRA_RETURN_HOME, returnHome) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } @@ -215,6 +223,7 @@ class MainActivity : ComponentActivity() { when (intent.action) { ACTION_NEW_TASK -> return NavRequest.NewTask( listId = intent.getLongExtra(EXTRA_PRESET_LIST_ID, -1L).takeIf { it > 0L }, + returnHome = true, ) ACTION_TODAY -> return NavRequest.OpenSmart(SmartList.TODAY) ACTION_OPEN_SMART -> intent.getStringExtra(EXTRA_SMART_LIST) @@ -229,12 +238,12 @@ class MainActivity : ComponentActivity() { if (intent.action == Intent.ACTION_SEND && intent.type?.startsWith("text/plain") == true) { val shared = intent.getStringExtra(Intent.EXTRA_SUBJECT)?.takeIf { it.isNotBlank() } ?: intent.getStringExtra(Intent.EXTRA_TEXT) - return NavRequest.NewTask(shared?.trim()?.take(SHARED_TITLE_LIMIT)) + return NavRequest.NewTask(shared?.trim()?.take(SHARED_TITLE_LIMIT), returnHome = true) } val taskId = intent.getLongExtra(EXTRA_TASK_ID, -1L).takeIf { it > 0L } ?: return null val occurrence = intent.getLongExtra(EXTRA_OCCURRENCE_START, NO_OCCURRENCE) .takeIf { it != NO_OCCURRENCE } - return NavRequest.OpenTask(taskId, occurrence) + return NavRequest.OpenTask(taskId, occurrence, intent.getBooleanExtra(EXTRA_RETURN_HOME, false)) } } } diff --git a/app/src/main/java/de/jeanlucmakiola/agendula/ui/RootScreen.kt b/app/src/main/java/de/jeanlucmakiola/agendula/ui/RootScreen.kt index 0e6da56..86e4a59 100644 --- a/app/src/main/java/de/jeanlucmakiola/agendula/ui/RootScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/agendula/ui/RootScreen.kt @@ -46,6 +46,7 @@ fun RootScreen( modifier: Modifier = Modifier, navRequest: NavRequest? = null, onNavRequestConsumed: () -> Unit = {}, + onReturnHome: () -> Unit = {}, permissionViewModel: PermissionViewModel = hiltViewModel(), ) { val permission by permissionViewModel.state.collectAsStateWithLifecycle() @@ -86,6 +87,7 @@ fun RootScreen( modifier = modifier, navRequest = navRequest, onNavRequestConsumed = onNavRequestConsumed, + onReturnHome = onReturnHome, ) } } @@ -101,6 +103,7 @@ private fun ReadyGate( modifier: Modifier = Modifier, navRequest: NavRequest? = null, onNavRequestConsumed: () -> Unit = {}, + onReturnHome: () -> Unit = {}, onboardingViewModel: OnboardingViewModel = hiltViewModel(), ) { val done by onboardingViewModel.done.collectAsStateWithLifecycle() @@ -113,6 +116,7 @@ private fun ReadyGate( modifier = modifier.fillMaxSize().background(MaterialTheme.colorScheme.surface), navRequest = navRequest, onNavRequestConsumed = onNavRequestConsumed, + onReturnHome = onReturnHome, ) false -> ReadableWidth(modifier) { OnboardingFlow(modifier = Modifier.fillMaxSize(), viewModel = onboardingViewModel) diff --git a/app/src/main/java/de/jeanlucmakiola/agendula/ui/navigation/AgendulaNavHost.kt b/app/src/main/java/de/jeanlucmakiola/agendula/ui/navigation/AgendulaNavHost.kt index 0365733..5500760 100644 --- a/app/src/main/java/de/jeanlucmakiola/agendula/ui/navigation/AgendulaNavHost.kt +++ b/app/src/main/java/de/jeanlucmakiola/agendula/ui/navigation/AgendulaNavHost.kt @@ -7,6 +7,11 @@ import androidx.compose.animation.slideInHorizontally import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.setValue +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.collectAsState import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.ui.Modifier @@ -46,8 +51,19 @@ fun AgendulaNavHost( modifier: Modifier = Modifier, navRequest: NavRequest? = null, onNavRequestConsumed: () -> Unit = {}, + onReturnHome: () -> Unit = {}, ) { val nav = rememberNavController() + // The entry a widget, tile, shortcut or share opened; closing it leaves the app (#93). + var returnHomeEntry by rememberSaveable { mutableStateOf(null) } + val backStack by nav.currentBackStack.collectAsState() + LaunchedEffect(backStack, returnHomeEntry) { + val id = returnHomeEntry ?: return@LaunchedEffect + if (backStack.none { it.id == id }) { + returnHomeEntry = null + onReturnHome() + } + } LaunchedEffect(navRequest) { when (navRequest) { @@ -71,6 +87,9 @@ fun AgendulaNavHost( } is NavRequest.Import -> nav.navigate(Dest.Import.build(navRequest.uri)) } + val returnHome = (navRequest as? NavRequest.NewTask)?.returnHome == true || + (navRequest as? NavRequest.OpenTask)?.returnHome == true + returnHomeEntry = if (returnHome) nav.currentBackStackEntry?.id else null onNavRequestConsumed() } diff --git a/app/src/main/java/de/jeanlucmakiola/agendula/ui/navigation/NavRequest.kt b/app/src/main/java/de/jeanlucmakiola/agendula/ui/navigation/NavRequest.kt index 3743cb3..5adf6df 100644 --- a/app/src/main/java/de/jeanlucmakiola/agendula/ui/navigation/NavRequest.kt +++ b/app/src/main/java/de/jeanlucmakiola/agendula/ui/navigation/NavRequest.kt @@ -9,8 +9,15 @@ import de.jeanlucmakiola.agendula.domain.SmartList * [AgendulaNavHost], which pushes it over whatever is showing. */ sealed interface NavRequest { - /** One task's detail; [occurrenceStart] (epoch millis) picks the occurrence of a series. */ - data class OpenTask(val taskId: Long, val occurrenceStart: Long? = null) : NavRequest + /** + * One task's detail; [occurrenceStart] (epoch millis) picks the occurrence of a series. + * [returnHome] leaves the app once the detail is closed (a widget row, #93). + */ + data class OpenTask( + val taskId: Long, + val occurrenceStart: Long? = null, + val returnHome: Boolean = false, + ) : NavRequest /** Settings → Accounts, where a sync report's detail lives. */ data object OpenAccounts : NavRequest @@ -21,8 +28,13 @@ sealed interface NavRequest { /** * A new task, optionally titled with text shared from another app, or * preset to a list — a widget's own add button, when it shows one list. + * [returnHome] leaves the app once the form is closed (#93). */ - data class NewTask(val title: String? = null, val listId: Long? = null) : NavRequest + data class NewTask( + val title: String? = null, + val listId: Long? = null, + val returnHome: Boolean = false, + ) : NavRequest /** One of the smart lists — the launcher's "Today" shortcut, a widget header. */ data class OpenSmart(val list: SmartList) : NavRequest diff --git a/app/src/main/java/de/jeanlucmakiola/agendula/widget/TaskWidget.kt b/app/src/main/java/de/jeanlucmakiola/agendula/widget/TaskWidget.kt index c4eedcf..247d762 100644 --- a/app/src/main/java/de/jeanlucmakiola/agendula/widget/TaskWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/agendula/widget/TaskWidget.kt @@ -328,7 +328,7 @@ internal fun AgendaTaskRow( val context = LocalContext.current val title = row.title.ifBlank { context.getString(R.string.task_untitled) } val open = actionStartActivity( - MainActivity.taskIntent(context, row.taskId, row.occurrenceStart?.toEpochMilliseconds()), + MainActivity.taskIntent(context, row.taskId, row.occurrenceStart?.toEpochMilliseconds(), returnHome = true), ) val onColor = if (colored) GlanceColorProvider(Color.White) else GlanceTheme.colors.onSurface val onColorVariant = if (colored) GlanceColorProvider(Color.White.copy(alpha = 0.8f)) else GlanceTheme.colors.onSurfaceVariant diff --git a/app/src/main/java/de/jeanlucmakiola/agendula/widget/UpNextWidget.kt b/app/src/main/java/de/jeanlucmakiola/agendula/widget/UpNextWidget.kt index ad3098a..0336f9e 100644 --- a/app/src/main/java/de/jeanlucmakiola/agendula/widget/UpNextWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/agendula/widget/UpNextWidget.kt @@ -339,7 +339,7 @@ private fun ListDot(color: Int) { } private fun openTask(context: Context, row: WidgetTaskRow): Action = - actionStartActivity(MainActivity.taskIntent(context, row.taskId, row.occurrenceStart?.toEpochMilliseconds())) + actionStartActivity(MainActivity.taskIntent(context, row.taskId, row.occurrenceStart?.toEpochMilliseconds(), returnHome = true)) private fun completeTask(row: WidgetTaskRow): Action = actionRunCallback(