diff options
| author | Lena <lena@omega> | 2026-06-01 00:00:00 +0000 |
|---|---|---|
| committer | Lena <lena@omega> | 2026-06-01 00:00:00 +0000 |
| commit | 72e37d2dac501faa300ca895c56c53eea43ae4ae (patch) | |
| tree | ca1a3f77d46f91f14b9c713f193af755f3d1fa2f /app/src/main/java/invalid | |
| parent | 7e04941bccb2683f8a6e3ee38a99c50129234dd1 (diff) | |
| download | rsend-72e37d2dac501faa300ca895c56c53eea43ae4ae.tar.gz | |
app: add a folder picker for local paths0.1.0
Typing absolute paths is error-prone; let the user browse real
filesystem folders and pick one. Stays on real paths (no SAF) so
rsync keeps operating on POSIX paths.
Diffstat (limited to 'app/src/main/java/invalid')
| -rw-r--r-- | app/src/main/java/invalid/lena/rsend/FolderEditActivity.kt | 21 | ||||
| -rw-r--r-- | app/src/main/java/invalid/lena/rsend/FolderPickerActivity.kt | 89 |
2 files changed, 110 insertions, 0 deletions
diff --git a/app/src/main/java/invalid/lena/rsend/FolderEditActivity.kt b/app/src/main/java/invalid/lena/rsend/FolderEditActivity.kt index 2fe2b1b..c61276c 100644 --- a/app/src/main/java/invalid/lena/rsend/FolderEditActivity.kt +++ b/app/src/main/java/invalid/lena/rsend/FolderEditActivity.kt @@ -1,11 +1,14 @@ package invalid.lena.rsend +import android.app.Activity +import android.content.Intent import android.os.Bundle import android.view.View import android.widget.Button import android.widget.EditText import android.widget.Switch import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import java.io.File @@ -20,6 +23,17 @@ class FolderEditActivity : AppCompatActivity() { private lateinit var excludes: EditText private lateinit var delete: Switch + // The picker returns a real filesystem path; drop it into the local field and + // name the folder after its basename if the name is still blank. + private val pickFolder = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val path = result.data?.getStringExtra("path") ?: return@registerForActivityResult + local.setText(path) + if (name.text.isNullOrBlank()) name.setText(File(path).name) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_folder) @@ -43,6 +57,13 @@ class FolderEditActivity : AppCompatActivity() { delete.isChecked = f.delete } + findViewById<Button>(R.id.browse).setOnClickListener { + pickFolder.launch( + Intent(this, FolderPickerActivity::class.java) + .putExtra("start", local.text.toString().trim()) + ) + } + findViewById<Button>(R.id.save).setOnClickListener { saveFolder(); finish() } val del = findViewById<Button>(R.id.removeFolder) del.visibility = if (index < 0) View.GONE else View.VISIBLE diff --git a/app/src/main/java/invalid/lena/rsend/FolderPickerActivity.kt b/app/src/main/java/invalid/lena/rsend/FolderPickerActivity.kt new file mode 100644 index 0000000..1adccf3 --- /dev/null +++ b/app/src/main/java/invalid/lena/rsend/FolderPickerActivity.kt @@ -0,0 +1,89 @@ +package invalid.lena.rsend + +import android.content.Intent +import android.os.Bundle +import android.os.Environment +import android.os.storage.StorageManager +import android.widget.Button +import android.widget.TextView +import androidx.activity.addCallback +import androidx.appcompat.app.AppCompatActivity +import java.io.File + +// FolderPickerActivity browses the real filesystem and returns one directory's +// absolute path in the "path" result extra. The app holds all-files access, so +// rsync works on real paths; this picker stays on real paths too (no SAF, no +// content URIs). Browsing is rooted at primary shared storage and cannot go +// above it; SD cards and other volumes are still reachable by typing the path. +class FolderPickerActivity : AppCompatActivity() { + + private lateinit var pathView: TextView + private lateinit var list: android.widget.LinearLayout + private lateinit var root: File + private lateinit var current: File + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_picker) + setSupportActionBar(findViewById(R.id.toolbar)) + title = "Pick folder" + fitSystemBars() + pathView = findViewById(R.id.currentPath) + list = findViewById(R.id.list) + + root = getSystemService(StorageManager::class.java).primaryStorageVolume.directory + ?: Environment.getExternalStorageDirectory() + + // Start where the field already points, if that is a directory under the + // root; otherwise at the root itself. + val start = intent.getStringExtra("start")?.let { File(it) } + current = if (start != null && start.isDirectory && + start.absolutePath.startsWith(root.absolutePath)) start else root + + findViewById<Button>(R.id.use).setOnClickListener { + setResult(RESULT_OK, Intent().putExtra("path", current.absolutePath)) + finish() + } + findViewById<Button>(R.id.cancel).setOnClickListener { finish() } + + // Hardware back walks up the tree until the root, then leaves the screen. + onBackPressedDispatcher.addCallback(this) { + if (current.absolutePath != root.absolutePath) { + current = current.parentFile ?: root + render() + } else { + isEnabled = false + onBackPressedDispatcher.onBackPressed() + } + } + + render() + } + + private fun render() { + pathView.text = current.absolutePath + list.removeAllViews() + + if (current.absolutePath != root.absolutePath && current.parentFile != null) { + addRow("..", current.parentFile!!) + } + + val dirs = current.listFiles { f -> f.isDirectory } + ?.sortedBy { it.name.lowercase() } ?: emptyList() + for (d in dirs) addRow(d.name, d) + + if (dirs.isEmpty()) { + list.addView(TextView(this).apply { + text = "No subfolders here. Tap \"Use this folder\" to pick it." + setTextAppearance(R.style.TextAppearance_Rsend_Caption) + }) + } + } + + private fun addRow(label: String, dir: File) { + val row = layoutInflater.inflate(R.layout.item_dir, list, false) + row.findViewById<TextView>(R.id.dirName).text = label + row.setOnClickListener { current = dir; render() } + list.addView(row) + } +} |