From 69d4257a1891f8c55fe3a4c2b517e0d7deb60ee3 Mon Sep 17 00:00:00 2001 From: Amir Khodak Date: Tue, 26 May 2026 01:12:42 +0000 Subject: [PATCH] v1.0.55: SAF system folder picker (same as Autosync) Tapping the local folder field now opens Android's native folder picker via ACTION_OPEN_DOCUMENT_TREE. The picked content:// URI gets persistent read/write permission and is stored as-is; the existing Saf backend handles all sync I/O through it. "Browse manually" link kept for the raw-path custom browser. Co-Authored-By: Claude Sonnet 4.6 --- .../com/syncflow/ui/addpair/AddPairScreen.kt | 46 ++++++++++++++----- version.properties | 4 +- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/app/src/main/kotlin/com/syncflow/ui/addpair/AddPairScreen.kt b/app/src/main/kotlin/com/syncflow/ui/addpair/AddPairScreen.kt index b3a00f4..5326022 100644 --- a/app/src/main/kotlin/com/syncflow/ui/addpair/AddPairScreen.kt +++ b/app/src/main/kotlin/com/syncflow/ui/addpair/AddPairScreen.kt @@ -1,5 +1,8 @@ package com.syncflow.ui.addpair +import android.content.Intent +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -12,6 +15,7 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -28,9 +32,20 @@ fun AddPairScreen(onDone: () -> Unit, vm: AddPairViewModel = hiltViewModel()) { val s by vm.state.collectAsState() LaunchedEffect(s.done) { if (s.done) onDone() } + val context = LocalContext.current var showRemoteBrowser by remember { mutableStateOf(false) } var showLocalBrowser by remember { mutableStateOf(false) } + val safLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + if (uri != null) { + context.contentResolver.takePersistableUriPermission( + uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION, + ) + vm.update { copy(localPath = uri.toString()) } + } + } + if (showLocalBrowser) { LocalBrowserDialog( initialPath = s.localPath.ifBlank { "" }, @@ -105,17 +120,26 @@ fun AddPairScreen(onDone: () -> Unit, vm: AddPairViewModel = hiltViewModel()) { Spacer(Modifier.height(4.dp)) - // Local folder - Box(modifier = Modifier.fillMaxWidth()) { - OutlinedTextField( - value = uriToDisplay(s.localPath), onValueChange = {}, - label = { Text("Local folder") }, - leadingIcon = { Icon(Icons.Default.PhoneAndroid, null) }, - trailingIcon = { Icon(Icons.Default.FolderOpen, null) }, - readOnly = true, singleLine = true, modifier = Modifier.fillMaxWidth(), - placeholder = { Text("Tap to choose folder…") }, - ) - Box(modifier = Modifier.matchParentSize().clickable { showLocalBrowser = true }) + // Local folder — tap opens Android system picker (SAF), same as Autosync + Column { + Box(modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = uriToDisplay(s.localPath), onValueChange = {}, + label = { Text("Local folder") }, + leadingIcon = { Icon(Icons.Default.PhoneAndroid, null) }, + trailingIcon = { Icon(Icons.Default.FolderOpen, null) }, + readOnly = true, singleLine = true, modifier = Modifier.fillMaxWidth(), + placeholder = { Text("Tap to choose folder…") }, + ) + Box(modifier = Modifier.matchParentSize().clickable { safLauncher.launch(null) }) + } + TextButton( + onClick = { showLocalBrowser = true }, + modifier = Modifier.align(Alignment.End), + contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp), + ) { + Text("Browse manually", style = MaterialTheme.typography.labelSmall) + } } // Remote folder diff --git a/version.properties b/version.properties index b3c963e..6030032 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -VERSION_NAME=1.0.54 -VERSION_CODE=55 +VERSION_NAME=1.0.55 +VERSION_CODE=56