feat(files): remember the Move and Copy target folder on the device - #277
Conversation
Web's folder picker has a "Remember target folder" toggle for Move and Make a copy, kept in web's own /config. Android always opened at root. The mobile picker now shows the toggle, off by default. On, Move here or Copy here records the chosen folder's path, and the next picker opens there; Back reads each ancestor as it reaches it. A remembered folder that can't be read reopens at root, one moved since keeps only root above it, and a move never opens inside the item it moves. Both values live in private SharedPreferences per signed-in account, and the auth controller's local-session cleanup clears them on sign-out or an expired session. TV has no Move picker. Closes #276
…older MobileMoveTargetProofTest drives the production Files route, controller and on-device store over a faked repository. FilesMoveRecoveryUiProofTest follows complete() now returning a transition.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Not covered: a real sign-out on the device (the emulator app has no live sign-in). The unit tests cover the cleanup call. Written by an agent (Claude Code, Opus 5.5). |
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
It makes intricate, interdependent changes to the core move/copy picker state machine, per-account persistence, and auth session cleanup that warrant final human review despite the thorough test coverage.
Review effort: Balanced
Findings: None
What changed in this PR
This PR ports put.io web's "Remember target folder" toggle to the mobile Move and Make-a-copy folder picker (closing #276). When enabled, the picker reopens at the folder last chosen, lazily reading each ancestor as the user walks Back toward root, with graceful fallbacks when the remembered folder was deleted, moved, or renamed. The setting is persisted per signed-in account in private SharedPreferences and cleared on sign-out or session expiry. TV is unaffected (it has no Move picker).
Changes:
- New
FilesMoveTargetMemorydomain model plus aFilesMoveTargetStoreinterface, with the move-destination state machine extended to open at a remembered path (opensRememberedTarget,completeRememberedTarget, lazyloadUnreadFolder, controllerstartPath). - Mobile UI toggle in
MobileFilesMoveDestination, wiring inMobileFilesRoute/MobileNavigation, and on-device persistence viaMobileMoveTargetStore. - Per-account cleanup hooked into
MobileAuthController/MobileOAuthRuntime, plus extensive unit/UI/synthetic-proof tests andbehavior.md/harness.mddocumentation.
| File | Description |
|---|---|
app/src/main/kotlin/io/putdotio/android/files/FilesMoveTargetMemory.kt |
New model + store interface; startPath/chosen encode the remember rules. |
app/src/main/kotlin/io/putdotio/android/files/FilesMoveDestinationState.kt |
complete() now returns a transition; adds remembered-target dispatch and lazy ancestor loading. |
app/src/main/kotlin/io/putdotio/android/files/FilesMoveDestinationStart.kt |
Remembered-target completion: root fallback, current-name refresh, moved-folder handling. |
app/src/main/kotlin/io/putdotio/android/files/FilesMoveDestinationController.kt |
Accepts startPath, builds unread-ancestor stack, chains follow-up requests on completion. |
app/src/mobile/kotlin/io/putdotio/android/files/MobileMoveTargetStore.kt |
Per-account SharedPreferences JSON store with clearAll and damaged-record fallback. |
app/src/mobile/kotlin/io/putdotio/android/files/MobileFilesRoute.kt |
Reads/writes memory per picker, passes startPath, wires toggle callbacks. |
app/src/mobile/kotlin/io/putdotio/android/files/MobileFilesMoveDestination.kt |
Adds the "Remember target folder" switch row with accessible toggleable semantics. |
app/src/mobile/kotlin/io/putdotio/android/MobileNavigation.kt |
Constructs the account-scoped MobileMoveTargetStore and passes it to the route. |
app/src/mobile/kotlin/io/putdotio/android/auth/MobileAuthController.kt |
Adds clearAccountLocalState, invoked in clearLocalSession. |
app/src/mobile/kotlin/io/putdotio/android/auth/MobileOAuthRuntime.kt |
Wires cleanup to MobileMoveTargetStore.clearAll. |
app/src/mobile/res/values/strings.xml |
New mobile_files_move_remember_target string. |
app/src/test/.../FilesMoveTargetMemoryTest.kt, FilesMoveDestinationControllerTest.kt |
Unit coverage for memory rules and controller navigation/fallbacks. |
app/src/testMobile/.../MobileMoveTargetStoreTest.kt, InMemoryMoveTargetStore.kt, MobileFilesMoveTest.kt, MobileFilesCopyTest.kt, MobileAuthControllerTest.kt |
Store, UI, and sign-out-cleanup coverage. |
app/src/androidTestMobile/.../MobileMoveTargetProofTest.kt, FilesMoveRecoveryUiProofTest.kt |
New synthetic device proof; recovery proof updated for the new complete() return type. |
docs/behavior.md, docs/harness.md |
Documents the new behavior and proof lane. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Keep a rejected session visible instead of falling back to root, take a remembered ancestor's current name and place when Back reads it, and reopen at root when a remembered folder now sits directly inside the item being moved.
|
Local review (slopguard, Codex gpt-6.1-sol, high): 4 findings on the first pass, 3 fixed and 1 partly fixed in 2fc01cf. The re-review of 2fc01cf raised 2 more, both rejected.
Negative control: with the fixes reverted, exactly the 4 new tests fail. Written by an agent (Claude Code, Opus 5.5). |







Change
The mobile Move and Make a copy picker gains web's "Remember target folder" toggle, off by default, so the next picker opens at the last chosen folder.
Closes #276
Validation
./gradlew verifyand the debug assembles in CIMobileMoveTargetProofTestonputio-phone, synthetic (screenshots)Written by an agent (Claude Code, Opus 5.5)