diff --git a/app/src/main/java/com/chiller3/bcr/Preferences.kt b/app/src/main/java/com/chiller3/bcr/Preferences.kt
index 85839cc20..6cc0f3625 100644
--- a/app/src/main/java/com/chiller3/bcr/Preferences.kt
+++ b/app/src/main/java/com/chiller3/bcr/Preferences.kt
@@ -3,9 +3,13 @@ package com.chiller3.bcr
import android.content.Context
import android.content.Intent
import android.net.Uri
+import android.os.Environment
+import android.provider.DocumentsContract
import android.util.Log
import androidx.core.content.edit
import androidx.preference.PreferenceManager
+import com.chiller3.bcr.extension.DOCUMENTSUI_AUTHORITY
+import com.chiller3.bcr.extension.safTreeToDocument
import com.chiller3.bcr.format.Format
import com.chiller3.bcr.format.SampleRate
import com.chiller3.bcr.output.Retention
@@ -164,6 +168,24 @@ class Preferences(private val context: Context) {
val outputDirOrDefault: Uri
get() = outputDir ?: Uri.fromFile(defaultOutputDir)
+ /**
+ * Build an [Intent] for opening DocumentsUI to the user-specified output directory or the
+ * default if none was set.
+ */
+ val outputDirOrDefaultIntent: Intent
+ get() = Intent(Intent.ACTION_VIEW).apply {
+ val uri = outputDir?.safTreeToDocument() ?: run {
+ // Opening a file:// URI will fail with FileUriExposedException. We need to hackily
+ // build our own URI for the directory. Luckily, the implementation details have
+ // never changed...
+ val externalDir = Environment.getExternalStorageDirectory()
+ val relPath = defaultOutputDir.relativeTo(externalDir)
+
+ DocumentsContract.buildDocumentUri(DOCUMENTSUI_AUTHORITY, "primary:$relPath")
+ }
+ setDataAndType(uri, "vnd.android.document/directory")
+ }
+
/** The user-specified filename template. */
var filenameTemplate: Template?
get() = prefs.getString(PREF_FILENAME_TEMPLATE, null)?.let { Template(it) }
diff --git a/app/src/main/java/com/chiller3/bcr/extension/UriExtensions.kt b/app/src/main/java/com/chiller3/bcr/extension/UriExtensions.kt
index 0b3d62fb9..22115bd28 100644
--- a/app/src/main/java/com/chiller3/bcr/extension/UriExtensions.kt
+++ b/app/src/main/java/com/chiller3/bcr/extension/UriExtensions.kt
@@ -2,14 +2,17 @@ package com.chiller3.bcr.extension
import android.content.ContentResolver
import android.net.Uri
+import android.provider.DocumentsContract
import android.telecom.PhoneAccount
+val DOCUMENTSUI_AUTHORITY = "com.android.externalstorage.documents"
+
val Uri.formattedString: String
get() = when (scheme) {
ContentResolver.SCHEME_FILE -> path!!
ContentResolver.SCHEME_CONTENT -> {
val prefix = when (authority) {
- "com.android.externalstorage.documents" -> ""
+ DOCUMENTSUI_AUTHORITY -> ""
// Include the authority to reduce ambiguity when this isn't a SAF URI provided by
// Android's local filesystem document provider
else -> "[$authority] "
@@ -34,3 +37,10 @@ val Uri.phoneNumber: String?
PhoneAccount.SCHEME_TEL -> schemeSpecificPart
else -> null
}
+
+fun Uri.safTreeToDocument(): Uri {
+ require(scheme == ContentResolver.SCHEME_CONTENT) { "Not a content URI" }
+
+ val documentId = DocumentsContract.getTreeDocumentId(this)
+ return DocumentsContract.buildDocumentUri(authority, documentId)
+}
diff --git a/app/src/main/java/com/chiller3/bcr/settings/SettingsFragment.kt b/app/src/main/java/com/chiller3/bcr/settings/SettingsFragment.kt
index bccf00a3f..cef9d5dbd 100644
--- a/app/src/main/java/com/chiller3/bcr/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/chiller3/bcr/settings/SettingsFragment.kt
@@ -1,5 +1,6 @@
package com.chiller3.bcr.settings
+import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
@@ -21,6 +22,7 @@ import com.chiller3.bcr.output.Retention
import com.chiller3.bcr.rule.RecordRulesActivity
import com.chiller3.bcr.view.LongClickablePreference
import com.chiller3.bcr.view.OnPreferenceLongClickListener
+import com.google.android.material.snackbar.Snackbar
class SettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener, OnPreferenceLongClickListener,
@@ -28,7 +30,7 @@ class SettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChan
private lateinit var prefs: Preferences
private lateinit var prefCallRecording: SwitchPreferenceCompat
private lateinit var prefRecordRules: Preference
- private lateinit var prefOutputDir: Preference
+ private lateinit var prefOutputDir: LongClickablePreference
private lateinit var prefOutputFormat: Preference
private lateinit var prefInhibitBatteryOpt: SwitchPreferenceCompat
private lateinit var prefVersion: LongClickablePreference
@@ -68,6 +70,7 @@ class SettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChan
prefOutputDir = findPreference(Preferences.PREF_OUTPUT_DIR)!!
prefOutputDir.onPreferenceClickListener = this
+ prefOutputDir.onPreferenceLongClickListener = this
refreshOutputDir()
prefOutputFormat = findPreference(Preferences.PREF_OUTPUT_FORMAT)!!
@@ -189,6 +192,18 @@ class SettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChan
override fun onPreferenceLongClick(preference: Preference): Boolean {
when (preference) {
+ prefOutputDir -> {
+ try {
+ startActivity(prefs.outputDirOrDefaultIntent)
+ } catch (e: ActivityNotFoundException) {
+ Snackbar.make(
+ requireView(),
+ R.string.documentsui_not_found,
+ Snackbar.LENGTH_LONG,
+ ).show()
+ }
+ return true
+ }
prefVersion -> {
prefs.isDebugMode = !prefs.isDebugMode
refreshVersion()
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d46fcf538..ba3c15c2b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -13,7 +13,7 @@
Configure which calls should be automatically recorded.
Output directory
- Pick a directory to store recordings.
+ Pick a directory to store recordings. Long press to open in file manager.
Output format
Select an encoding format for the recordings.
@@ -118,4 +118,7 @@
Call recording
+
+
+ Android\'s builtin file manager (DocumentsUI) is unavailable.
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index eeeacea81..80e93fcb0 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -16,7 +16,7 @@
app:summary="@string/pref_record_rules_desc"
app:iconSpaceReserved="false" />
-