diff options
Diffstat (limited to 'app/src')
| -rw-r--r-- | app/src/main/AndroidManifest.xml | 4 | ||||
| -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 | ||||
| -rw-r--r-- | app/src/main/res/layout/activity_folder.xml | 26 | ||||
| -rw-r--r-- | app/src/main/res/layout/activity_picker.xml | 61 | ||||
| -rw-r--r-- | app/src/main/res/layout/item_dir.xml | 27 |
6 files changed, 223 insertions, 5 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 31c8b72..e146f29 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,10 @@ android:exported="false" /> <activity + android:name=".FolderPickerActivity" + android:exported="false" /> + + <activity android:name=".ScheduleActivity" android:exported="false" /> 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) + } +} diff --git a/app/src/main/res/layout/activity_folder.xml b/app/src/main/res/layout/activity_folder.xml index 1a15f00..1381529 100644 --- a/app/src/main/res/layout/activity_folder.xml +++ b/app/src/main/res/layout/activity_folder.xml @@ -46,14 +46,30 @@ android:textAppearance="@style/TextAppearance.Rsend.Caption" android:text="Local path" /> - <EditText - android:id="@+id/local" - style="@style/Widget.Rsend.EditText" + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:inputType="textUri" - android:hint="/storage/emulated/0/DCIM" /> + android:orientation="horizontal" + android:gravity="center_vertical"> + + <EditText + android:id="@+id/local" + style="@style/Widget.Rsend.EditText" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:inputType="textUri" + android:hint="/storage/emulated/0/DCIM" /> + + <Button + android:id="@+id/browse" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/space_s" + android:text="Browse" /> + </LinearLayout> <TextView android:layout_width="match_parent" diff --git a/app/src/main/res/layout/activity_picker.xml b/app/src/main/res/layout/activity_picker.xml new file mode 100644 index 0000000..db4a044 --- /dev/null +++ b/app/src/main/res/layout/activity_picker.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/appRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <include layout="@layout/app_toolbar" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="vertical" + android:padding="@dimen/space_m"> + + <TextView + android:id="@+id/currentPath" + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.Rsend.Mono" + android:textSize="12sp" /> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <LinearLayout + android:id="@+id/list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" /> + </ScrollView> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="@dimen/space_m"> + + <Button + android:id="@+id/use" + style="@style/Widget.Rsend.Button.Primary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginEnd="@dimen/space_s" + android:text="Use this folder" /> + + <Button + android:id="@+id/cancel" + style="@style/Widget.Rsend.Button.Secondary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Cancel" /> + </LinearLayout> + </LinearLayout> +</LinearLayout> diff --git a/app/src/main/res/layout/item_dir.xml b/app/src/main/res/layout/item_dir.xml new file mode 100644 index 0000000..651224c --- /dev/null +++ b/app/src/main/res/layout/item_dir.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/Widget.Rsend.Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical" + android:clickable="true" + android:focusable="true" + android:foreground="?attr/selectableItemBackground"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginEnd="@dimen/space_m" + android:src="@drawable/ic_folder" + android:contentDescription="@null" /> + + <TextView + android:id="@+id/dirName" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:maxLines="1" + android:ellipsize="end" + android:textAppearance="@style/TextAppearance.Rsend.Title" /> +</LinearLayout> |