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" /> -