fix: intercepter le back système pour revenir à l'accueil
Le geste back Android part du bord même de l'écran : l'OS intercepte le geste avant le SwipeBack custom (zone de départ > 24 dp) et, sans callback back enregistré, l'activité est envoyée en arrière-plan vers le launcher au lieu de revenir à l'écran précédent. Enregistre un BackHandler par écran (Historique et Résultats retournent à l'accueil, la partie ouvre le dialogue d'abandon) et active le predictive back dans le manifest.
This commit is contained in:
6 files changed
+57
No files matched your search
@@ -4,6 +4,7 @@
|
|||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="Livrets"
|
android:label="Livrets"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:theme="@android:style/Theme.Material.NoActionBar">
|
android:theme="@android:style/Theme.Material.NoActionBar">
|
||||||
<activity
|
<activity
|
||||||
android:name=".AppActivity"
|
android:name=".AppActivity"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ compose-resources = { module = "org.jetbrains.compose.components:components-reso
|
|||||||
compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "compose-multiplatform" }
|
compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "compose-multiplatform" }
|
||||||
compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "compose-multiplatform" }
|
compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "compose-multiplatform" }
|
||||||
compose-ui-test = { module = "org.jetbrains.compose.ui:ui-test", version.ref = "compose-multiplatform" }
|
compose-ui-test = { module = "org.jetbrains.compose.ui:ui-test", version.ref = "compose-multiplatform" }
|
||||||
|
compose-backhandler = { module = "org.jetbrains.compose.ui:ui-backhandler", version.ref = "compose-multiplatform" }
|
||||||
compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" }
|
compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" }
|
||||||
androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
|
androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
|
||||||
androidx-fragmentKtx = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragmentKtx" }
|
androidx-fragmentKtx = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragmentKtx" }
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ kotlin {
|
|||||||
api(libs.compose.foundation)
|
api(libs.compose.foundation)
|
||||||
api(libs.compose.ui.tooling.preview)
|
api(libs.compose.ui.tooling.preview)
|
||||||
api(libs.compose.material3)
|
api(libs.compose.material3)
|
||||||
|
api(libs.compose.backhandler)
|
||||||
implementation(libs.kotlinx.datetime)
|
implementation(libs.kotlinx.datetime)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.backhandler.BackHandler
|
||||||
import com.livrets.data.HistoryRepository
|
import com.livrets.data.HistoryRepository
|
||||||
import com.livrets.game.GameConfig
|
import com.livrets.game.GameConfig
|
||||||
import com.livrets.game.GameResult
|
import com.livrets.game.GameResult
|
||||||
@@ -32,6 +33,13 @@ sealed interface Screen {
|
|||||||
data object History : Screen
|
data object History : Screen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Screen.backTarget(): Screen? = when (this) {
|
||||||
|
Screen.Home -> null
|
||||||
|
is Screen.Game -> null
|
||||||
|
is Screen.Results -> Screen.Home
|
||||||
|
Screen.History -> Screen.Home
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun App(
|
fun App(
|
||||||
filesDirPath: String,
|
filesDirPath: String,
|
||||||
@@ -48,6 +56,11 @@ fun App(
|
|||||||
HomeScreen(onPlay = {}, onHistory = {}, onLeaderboard = {})
|
HomeScreen(onPlay = {}, onHistory = {}, onLeaderboard = {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
screen.backTarget()?.let { target ->
|
||||||
|
@OptIn(androidx.compose.ui.ExperimentalComposeUiApi::class)
|
||||||
|
BackHandler { screen = target }
|
||||||
|
}
|
||||||
|
|
||||||
when (val current = screen) {
|
when (val current = screen) {
|
||||||
Screen.Home -> HomeScreen(
|
Screen.Home -> HomeScreen(
|
||||||
onPlay = { config -> screen = Screen.Game(config) },
|
onPlay = { config -> screen = Screen.Game(config) },
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.backhandler.BackHandler
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -52,6 +53,8 @@ fun GameScreen(
|
|||||||
LaunchedEffect(engine) { engine.start() }
|
LaunchedEffect(engine) { engine.start() }
|
||||||
|
|
||||||
var showQuitDialog by remember { mutableStateOf(false) }
|
var showQuitDialog by remember { mutableStateOf(false) }
|
||||||
|
@OptIn(androidx.compose.ui.ExperimentalComposeUiApi::class)
|
||||||
|
BackHandler { showQuitDialog = true }
|
||||||
if (showQuitDialog) {
|
if (showQuitDialog) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = { showQuitDialog = false },
|
onDismissRequest = { showQuitDialog = false },
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.livrets
|
||||||
|
|
||||||
|
import com.livrets.game.GameConfig
|
||||||
|
import com.livrets.game.GameResult
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
|
class BackNavigationTest {
|
||||||
|
|
||||||
|
private val config = GameConfig(questionCount = 10, secondsPerQuestion = 5, maxTable = 10)
|
||||||
|
|
||||||
|
private val result = GameResult(
|
||||||
|
date = kotlin.time.Instant.fromEpochMilliseconds(0L),
|
||||||
|
config = config,
|
||||||
|
records = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `back depuis l historique ramene a l accueil`() {
|
||||||
|
assertEquals(Screen.Home, Screen.History.backTarget())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `back depuis les resultats ramene a l accueil`() {
|
||||||
|
assertEquals(Screen.Home, Screen.Results(result).backTarget())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `pas de navigation back depuis l accueil`() {
|
||||||
|
assertNull(Screen.Home.backTarget())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `pas de navigation back depuis une partie`() {
|
||||||
|
assertNull(Screen.Game(config).backTarget())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user