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 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
package com.syncflow.ui.addpair
|
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.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@@ -12,6 +15,7 @@ import androidx.compose.material3.*
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
@@ -28,9 +32,20 @@ fun AddPairScreen(onDone: () -> Unit, vm: AddPairViewModel = hiltViewModel()) {
|
|||||||
val s by vm.state.collectAsState()
|
val s by vm.state.collectAsState()
|
||||||
LaunchedEffect(s.done) { if (s.done) onDone() }
|
LaunchedEffect(s.done) { if (s.done) onDone() }
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
var showRemoteBrowser by remember { mutableStateOf(false) }
|
var showRemoteBrowser by remember { mutableStateOf(false) }
|
||||||
var showLocalBrowser 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) {
|
if (showLocalBrowser) {
|
||||||
LocalBrowserDialog(
|
LocalBrowserDialog(
|
||||||
initialPath = s.localPath.ifBlank { "" },
|
initialPath = s.localPath.ifBlank { "" },
|
||||||
@@ -105,17 +120,26 @@ fun AddPairScreen(onDone: () -> Unit, vm: AddPairViewModel = hiltViewModel()) {
|
|||||||
|
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
|
|
||||||
// Local folder
|
// Local folder — tap opens Android system picker (SAF), same as Autosync
|
||||||
Box(modifier = Modifier.fillMaxWidth()) {
|
Column {
|
||||||
OutlinedTextField(
|
Box(modifier = Modifier.fillMaxWidth()) {
|
||||||
value = uriToDisplay(s.localPath), onValueChange = {},
|
OutlinedTextField(
|
||||||
label = { Text("Local folder") },
|
value = uriToDisplay(s.localPath), onValueChange = {},
|
||||||
leadingIcon = { Icon(Icons.Default.PhoneAndroid, null) },
|
label = { Text("Local folder") },
|
||||||
trailingIcon = { Icon(Icons.Default.FolderOpen, null) },
|
leadingIcon = { Icon(Icons.Default.PhoneAndroid, null) },
|
||||||
readOnly = true, singleLine = true, modifier = Modifier.fillMaxWidth(),
|
trailingIcon = { Icon(Icons.Default.FolderOpen, null) },
|
||||||
placeholder = { Text("Tap to choose folder…") },
|
readOnly = true, singleLine = true, modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
placeholder = { Text("Tap to choose folder…") },
|
||||||
Box(modifier = Modifier.matchParentSize().clickable { showLocalBrowser = true })
|
)
|
||||||
|
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
|
// Remote folder
|
||||||
|
|||||||
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
VERSION_NAME=1.0.54
|
VERSION_NAME=1.0.55
|
||||||
VERSION_CODE=55
|
VERSION_CODE=56
|
||||||
|
|||||||
Reference in New Issue
Block a user