As you mentioned Environment.getExternalStoragePublicDirectory
is marked deprecated. So there is no regular way to get the path to Downloads directory to save your file there. Alternatively you can use ACTION_CREATE_DOCUMENT
to show path picker and then use returned uri to write file to selected location.
This is how to show picker:
// Request code for creating a document.
const val CREATE_FILE = 1
private fun createFile(pickerInitialUri: Uri) {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/plain"
putExtra(Intent.EXTRA_TITLE, "sam.txt")
// Optionally, specify a URI for the directory that should be opened in
// the system file picker before your app creates the document.
putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, CREATE_FILE)
}
And this is how to get selected uri and write file:
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == CREATE_FILE && resultCode == Activity.RESULT_OK) {
// The result data contains a URI for the document or directory that
// the user selected.
resultData?.data?.also { outputUri ->
// Perform operations on the document using its URI.
FileInputStream(inputFile).use { inputStream ->
context.contentResolver.openFileDescriptor(outputUri, "w")?.use {
FileOutputStream(it.fileDescriptor).use { outputStream ->
FileUtils.copy(inputStream, outputStream)
}
}
}
}
}
}
More information can be found here.
EDIT:
To pick a directory to persist files ACTION_OPEN_DOCUMENT_TREE
can be used. Then use takePersistableUriPermission
method to take granted persistable permission to be able to use it after device restart. And then use DocumentFile
to execute file operations.
Open directory request:
private static final int OPEN_DIRECTORY_REQUEST_CODE = 1;
void openDirectory() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
}
Receive picked directory and take persistable permission:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == OPEN_DIRECTORY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri directoryUri = data.getData();
if (directoryUri == null)
return;
requireContext()
.getContentResolver()
.takePersistableUriPermission(directoryUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// persist picked uri to be able to reuse it later
} else
super.onActivityResult(requestCode, resultCode, data);
}
And at last persist the file:
private void persistFile(@NonNull Uri directoryUri,
@NonNull File fileToPersist,
@NonNull String mimeType,
@NonNull String displayName) {
DocumentFile dirFile = DocumentFile.fromSingleUri(requireContext(), directoryUri);
if (dirFile != null) {
DocumentFile file = dirFile.createFile(mimeType, displayName);
if (file != null) {
Uri outputUri = file.getUri();
try (ParcelFileDescriptor fd = requireContext().getContentResolver().openFileDescriptor(outputUri, "w")) {
if (fd != null) {
try (FileInputStream inputStream = new FileInputStream(fileToPersist)) {
try (FileOutputStream outputStream = new FileOutputStream(fd.getFileDescriptor())) {
FileUtils.copy(inputStream, outputStream);
}
}
}
} catch (Throwable th) {
th.printStackTrace();
}
}
}
}
Review this repo for an example of ACTION_CREATE_DOCUMENT
usage.
Uri.fromFile(file)
Replace byMediaStore.Files.getContentUri("external")
. – Claudiaclaudianif path of input file is irrelevant,
It is only irrelevant for obtaining an output uri from the media store. Of course it is relevant the moment you open an input streram to do the copy. – Claudiaclaudian