Skip to content

Commit 3a74ebe

Browse files
committed
feat: refine file-code confirmation and prepare 0.4.1
The receive-code design introduced in 0.4.0 made file transfers much simpler by removing the receiver-side Accept/Decline wait. This follow-up makes the sender experience and the surrounding source code match that simpler direction more clearly. Replace the centered file receive-code alert with a modal bottom sheet. Show the target device, four clear digit positions, and the number of selected files. Keep confirmation explicit: entering four digits enables the Send button, but the transfer does not start until the sender taps it. Simplify the idle Receive presentation and clarify the wording around the temporary file code on both Send and Receive. Keep the same application-session code and do not remember codes on the sender. Rename the outgoing raw file-stream abstraction from FileTransferSender to FileTransmitter across common code, Android, JVM, iOS, Koin bindings, and the outgoing controller. Rename the HTTP helper so it clearly says that it sends a file-offer request. These are readability changes and do not alter socket ownership, cancellation, streaming, framing, progress, timeouts, or receiver preparation. Prepare release 0.4.1 across all supported package targets: - Android version 0.4.1 with version code 5 - Desktop package version 0.4.1 - iOS marketing version 0.4.1 with build number 5 - Preview protocol version remains 1 Update the changelog, README, architecture, development guide, and project context. Clarify that 0.4.0 and 0.4.1 retain the same wire format, while the receive-code file flow remains incompatible with 0.3.0 and older builds.
1 parent 124a724 commit 3a74ebe

25 files changed

Lines changed: 296 additions & 157 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

77
## [Unreleased]
88

9+
## [0.4.1] - 2026-08-28
10+
11+
### Changed
12+
13+
- Replaced the centered file receive-code alert with a modal bottom sheet that shows the target device, four clear digit slots, and the selected file count.
14+
- Kept file sending explicit: entering four digits enables the Send button, and the transfer starts only after the sender confirms.
15+
- Simplified the idle Receive presentation and clarified the temporary file-code wording on Send and Receive.
16+
- Renamed the outgoing raw file-stream abstraction to `FileTransmitter` and made the HTTP file-offer helper name more explicit. These are internal readability changes and do not change the `0.4.0` wire format.
17+
- Prepared Android, Desktop, and iOS packages as `0.4.1`; Android and iOS build numbers are `5`. Preview protocol metadata remains version `1`.
18+
919
## [0.4.0] - 2026-08-28
1020

1121
### Changed
@@ -14,7 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
1424
- Displayed the same session receive code on both the Send and Receive screens so it is visible from the default screen.
1525
- File offers now include the entered code and receive an immediate accepted, invalid-code, busy, or preparation-failed response.
1626
- Removed file `UserDecision`, `CompletableDeferred`, incoming-offer state, decision timeout, and Accept/Cancel race while retaining operation IDs, TCP preparation timeout, cancellation, progress, framing, and cleanup.
17-
- Changed the file-offer wire format, so matching builds are required; preview protocol metadata intentionally remains version `1` for now.
27+
- Changed the file-offer wire format, making `0.4.0` incompatible with `0.3.0` and older builds; preview protocol metadata intentionally remains version `1` for now.
1828

1929
### Security
2030

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,15 @@ Platform file picker
130130
-> sender enters the receiver's temporary four-digit code
131131
-> POST /sync360/file/offer sends metadata and code
132132
-> idle receiver checks the code and prepares its TCP receiver immediately
133-
-> platform FileTransferSender opens an InputStream
133+
-> platform FileTransmitter opens an InputStream
134134
-> one raw TCP connection streams the accepted file batch
135135
-> platform DownloadsWriter saves each file
136136
-> receiver returns final success and completed-file count
137137
```
138138

139139
The receive code is generated in memory when a fresh application session starts. The same code is shown on the Send and Receive screens, so it is available from the default screen without switching tabs. It is not persisted, advertised, or remembered by the sender. It is a convenience check, not authentication or encryption.
140140

141-
This changes the file-offer request and response format. Builds containing this flow are not file-transfer compatible with `0.3.0` or older builds, even though the advertised preview protocol version intentionally remains `1` for now. Use matching builds on both devices.
141+
The receive-code file-offer format is not file-transfer compatible with `0.3.0` or older builds. Version `0.4.1` does not change the `0.4.0` wire format, so `0.4.0` and `0.4.1` can transfer files with each other. The advertised preview protocol version intentionally remains `1` for now, and discovery does not yet enforce this compatibility boundary.
142142

143143
One TCP socket is opened for the complete accepted batch. It begins with the operation ID as 16 raw UUID bytes; each file then begins with its index and promised byte count, followed by exactly that many bytes. The receiver checks the operation ID, index, and size before saving. The sender writes every file sequentially, flushes once after the complete batch, then reads one final success flag and completed-file count from the receiver. The count increases only after the platform Downloads writer successfully returns. The current shared payload buffer is 512 KiB; exact byte counts define file boundaries, so correctness does not depend on `flush()` calls or matching sender and receiver read chunks.
144144

androidApp/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ android {
3636
applicationId = "com.liftley.sync360"
3737
minSdk = libs.versions.android.minSdk.get().toInt()
3838
targetSdk = libs.versions.android.targetSdk.get().toInt()
39-
versionCode = 4
40-
versionName = "0.4.0"
39+
versionCode = 5
40+
versionName = "0.4.1"
4141
}
4242

4343
buildFeatures {

context.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Current shared transfer constants use a 512 KiB payload buffer, 5-second connect
7777

7878
## Important limitations
7979

80-
Sync360 currently uses cleartext local HTTP and raw TCP. Direct text has no receiver approval or operation ID. File operation IDs correlate protocol messages and sockets but do not authenticate a peer. A temporary four-digit receive code replaces file Accept/Decline, but it has no attempt throttling and is not authentication, encryption, or a checksum. The new file-offer format is incompatible with `0.3.0` and older builds while preview protocol metadata intentionally remains version `1`, so both devices must run matching builds. The current target-SDK-37 Android build also lacks Android 17's required local-network runtime-permission flow. Windows receiving depends on Windows Firewall allowing the application. Use development builds only on private networks you control.
80+
Sync360 currently uses cleartext local HTTP and raw TCP. Direct text has no receiver approval or operation ID. File operation IDs correlate protocol messages and sockets but do not authenticate a peer. A temporary four-digit receive code replaces file Accept/Decline, but it has no attempt throttling and is not authentication, encryption, or a checksum. The receive-code file-offer format is incompatible with `0.3.0` and older builds, while `0.4.0` and `0.4.1` share the same wire format. Preview protocol metadata intentionally remains version `1`, so discovery does not yet enforce this boundary. The current target-SDK-37 Android build also lacks Android 17's required local-network runtime-permission flow. Windows receiving depends on Windows Firewall allowing the application. Use development builds only on private networks you control.
8181

8282
For detailed and current information, read:
8383

desktopApp/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ compose.desktop {
3737
nativeDistributions {
3838
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
3939
packageName = "Sync360"
40-
packageVersion = "0.4.0"
40+
packageVersion = "0.4.1"
4141
appResourcesRootDir.set(
4242
project.layout.projectDirectory.dir("packaging/app-resources")
4343
)

docs/ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ A file offer contains the sender-entered four-digit receive code, operation iden
112112

113113
The receive code is generated once when the singleton incoming controller is created for a fresh application session. The Send and Receive ViewModels both read that same controller-owned value and render the same shared code card. It remains only in memory, is not advertised, and is not remembered by the sender. It is a convenience check rather than authentication because it has only 9,000 possible values and is sent over cleartext HTTP.
114114

115-
The added request field and structured response statuses change the file-offer wire format. This implementation is not file-transfer compatible with `0.3.0` or older builds. Protocol metadata intentionally remains version `1` during the current preview stage, so matching application builds are required even though discovery does not yet reject an older peer.
115+
The added request field and structured response statuses changed the file-offer wire format in `0.4.0`. This implementation is not file-transfer compatible with `0.3.0` or older builds. Version `0.4.1` keeps the `0.4.0` wire format, so those two versions can transfer files with each other. Protocol metadata intentionally remains version `1` during the current preview stage, and discovery does not yet reject an incompatible older peer.
116116

117117
A random operation ID still correlates the accepted file offer, explicit cancellation, and TCP connection. Cancellation succeeds only when both the operation ID and sender device ID match the active file state. Every platform receiver retains its 30-second timeout waiting for the first TCP connection, so an accepted offer cannot leave the receiver busy forever if the sender disappears. File contents remain in platform file readers and are not placed in the HTTP request.
118118

docs/DEVELOPMENT.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Windows:
5656

5757
## Preparing public packages
5858

59-
The current package version is `0.4.0`.
59+
The current package version is `0.4.1`.
6060

6161
Android release APKs must use the maintainer's permanent private signing key. Copy `keystore.properties.example` to the ignored `keystore.properties` file and set:
6262

@@ -89,7 +89,7 @@ The Windows `upgradeUuid` must remain unchanged for the lifetime of Sync360, and
8989
2. Open Sync360 on both devices and keep it in the foreground during current testing.
9090
3. Wait for the other device to appear on the Send screen.
9191
4. Test direct text delivery while idle and busy, the 100,000/100,001 boundaries, sender name, Copy, and Clear.
92-
5. Test the file receive-code dialog with correct, incorrect, incomplete, and non-numeric input.
92+
5. Test the file receive-code bottom sheet with correct, incorrect, incomplete, and non-numeric input.
9393
6. Confirm Send and Receive show the same code, and that a fresh application start creates a new code while navigation and recomposition do not change it.
9494
7. Test one file, multiple files, receiver-busy behavior, the first-connection timeout, and cancellation.
9595
8. Confirm completed files appear in Downloads.
@@ -125,7 +125,7 @@ Useful source locations:
125125
- `JvmNetworkServices` — current macOS/Linux JmDNS registration, discovery, repair cleanup, and IPv4/IPv6 LAN-interface selection.
126126
- `Sync360HttpServer` / `Sync360HttpClient` — direct text delivery and file control routes.
127127
- `OutgoingRequestsController` / `IncomingServerRequestsController` — send/receive coordination.
128-
- platform `FileTransferSender`, `FileTransferReceiver`, and `DownloadsWriter` implementations — file bytes and storage.
128+
- platform `FileTransmitter`, `FileTransferReceiver`, and `DownloadsWriter` implementations — file bytes and storage.
129129

130130
The Windows backend currently requires a 64-bit Desktop JVM, matching the project's Windows packaging target and the native ABI used by the binding.
131131

iosApp/Configuration/Config.xcconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ TEAM_ID=
33
PRODUCT_NAME=Sync360
44
PRODUCT_BUNDLE_IDENTIFIER=com.liftley.sync360.Sync360$(TEAM_ID)
55

6-
CURRENT_PROJECT_VERSION=4
7-
MARKETING_VERSION=0.4.0
6+
CURRENT_PROJECT_VERSION=5
7+
MARKETING_VERSION=0.4.1

shared/src/androidMain/kotlin/com/liftley/sync360/core/di/koin.android.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import com.liftley.sync360.data.file.DownloadsWriter
1010
import com.liftley.sync360.data.file.SelectedFileReader
1111
import com.liftley.sync360.data.network.discovery.AndroidNetworkServices
1212
import com.liftley.sync360.data.network.tcp.AndroidFileTransferReceiver
13-
import com.liftley.sync360.data.network.tcp.AndroidFileTransferSender
13+
import com.liftley.sync360.data.network.tcp.AndroidFileTransmitter
1414
import com.liftley.sync360.data.network.tcp.FileTransferReceiver
15-
import com.liftley.sync360.data.network.tcp.FileTransferSender
15+
import com.liftley.sync360.data.network.tcp.FileTransmitter
1616
import com.liftley.sync360.domain.local.LocalDeviceIdentityStore
1717
import com.liftley.sync360.domain.local.LocalDeviceInfoProvider
1818
import com.liftley.sync360.domain.repository.ClipboardProvider
@@ -30,8 +30,8 @@ val androidModule = module {
3030
single<SelectedFileReader> { AndroidSelectedFileReader(androidContext()) }
3131
single<DownloadsWriter<InputStream>> { AndroidDownloadsWriter(androidContext()) }
3232

33-
single<FileTransferSender> {
34-
AndroidFileTransferSender(
33+
single<FileTransmitter> {
34+
AndroidFileTransmitter(
3535
context = androidContext()
3636
)
3737
}

shared/src/androidMain/kotlin/com/liftley/sync360/data/network/tcp/AndroidFileTransferSender.kt renamed to shared/src/androidMain/kotlin/com/liftley/sync360/data/network/tcp/AndroidFileTransmitter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import java.net.Socket
1818
import java.util.concurrent.atomic.AtomicReference
1919
import kotlin.uuid.Uuid
2020

21-
class AndroidFileTransferSender(
21+
class AndroidFileTransmitter(
2222
private val context: Context
23-
) : FileTransferSender {
23+
) : FileTransmitter {
2424
private val activeSocket = AtomicReference<Socket?>(null)
2525

2626
override fun cancelCurrentFileTransfer() {

0 commit comments

Comments
 (0)