diff --git a/.github/nightly-notes/2026-11-07-fix-sync-connection-data-state.md b/.github/nightly-notes/2026-11-07-fix-sync-connection-data-state.md new file mode 100644 index 000000000..c8d1c27e5 --- /dev/null +++ b/.github/nightly-notes/2026-11-07-fix-sync-connection-data-state.md @@ -0,0 +1,2 @@ +## Fixed +- Sync: connection data you paste is kept in memory only, so it is cleared if Android closes the app in the background diff --git a/app/src/main/java/io/github/aedev/flow/ui/screens/sync/SyncSetupContent.kt b/app/src/main/java/io/github/aedev/flow/ui/screens/sync/SyncSetupContent.kt index a7437e260..0641309be 100644 --- a/app/src/main/java/io/github/aedev/flow/ui/screens/sync/SyncSetupContent.kt +++ b/app/src/main/java/io/github/aedev/flow/ui/screens/sync/SyncSetupContent.kt @@ -35,7 +35,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -164,7 +163,7 @@ internal fun SyncManualEntryContent(onSubmit: (String) -> Unit) { val context = LocalContext.current val clipboard = LocalClipboard.current val scope = rememberCoroutineScope() - var connectionData by rememberSaveable { mutableStateOf("") } + var connectionData by remember { mutableStateOf("") } val trimmedData = connectionData.trim() SyncStepHeader( diff --git a/app/src/test/java/io/github/aedev/flow/ui/screens/sync/SyncManualEntryTest.kt b/app/src/test/java/io/github/aedev/flow/ui/screens/sync/SyncManualEntryTest.kt new file mode 100644 index 000000000..a1c78b1b4 --- /dev/null +++ b/app/src/test/java/io/github/aedev/flow/ui/screens/sync/SyncManualEntryTest.kt @@ -0,0 +1,42 @@ +package io.github.aedev.flow.ui.screens.sync + +import android.app.Application +import android.content.Context +import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.junit4.StateRestorationTester +import androidx.compose.ui.test.junit4.v2.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performTextInput +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.aedev.flow.R +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config + +/** Pasting another device's connection data by hand. */ +@RunWith(AndroidJUnit4::class) +@Config(sdk = [34], application = Application::class, qualifiers = "w360dp-h1600dp") +class SyncManualEntryTest { + @get:Rule + val rule = createComposeRule() + + private val context: Context get() = ApplicationProvider.getApplicationContext() + + private fun text(id: Int) = context.getString(id) + + @Test + fun pasted_connection_data_is_not_kept_in_saved_state() { + val restoration = StateRestorationTester(rule) + restoration.setContent { SyncManualEntryContent(onSubmit = {}) } + rule.onNodeWithText(text(R.string.sync_connection_data_label)).performTextInput("typed data") + rule.onNodeWithText(text(R.string.sync_connect)).assertIsEnabled() + + restoration.emulateSavedInstanceStateRestore() + + rule.onNodeWithText("typed data").assertDoesNotExist() + rule.onNodeWithText(text(R.string.sync_connect)).assertIsNotEnabled() + } +}