aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/invalid')
-rw-r--r--app/src/main/java/invalid/lena/rsend/FolderEditActivity.kt21
-rw-r--r--app/src/main/java/invalid/lena/rsend/FolderPickerActivity.kt89
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)
+ }
+}