I want to save a file (Image, Audio, Video, Document) from the internal storage of my application to the Public directories depending on the File type. So i made this function
#RequiresApi(Build.VERSION_CODES.Q)
private fun saveFileUsingMediaStore(file: File, mimeType: String, fileName: String, destinationDirectory: String) {
var uri: Uri? = null
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, destinationDirectory)
}
runCatching {
with(appContext.contentResolver) {
insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)?.let {
uri = it
file.inputStream().use { input ->
openOutputStream(it)?.use { output ->
input.copyTo(output, DEFAULT_BUFFER_SIZE)
} ?: throw IOException("Failed to open output stream.")
}
} ?: {
toast("Failed to create MediaStore record")
//throw IOException("Failed to create new MediaStore record.")
}
}
}.getOrElse {
uri?.let { failedUri ->
toast("Delete orphan entry")
appContext.contentResolver.delete(failedUri, null, null)
}
}
}
How ever, when the code reach to the insert method, the code stops and does not do anything. Its like the insert never happens. Is there something wrong?
The problem solved when i changed the MediaStore.Downloads.EXTERNAL_CONTENT_URI with MediaStore.*.getContentUri("external")
The * gets its value by the fileType of the file that i want to save.
val externalUri = when (fileType) {
IMAGE_TYPE -> MediaStore.Images.Media.getContentUri("external")
AUDIO_TYPE -> MediaStore.Audio.Media.getContentUri("external")
VIDEO_TYPE -> MediaStore.Files.getContentUri("external")
DOCUMENT_TYPE -> MediaStore.Files.getContentUri("external")
else -> MediaStore.Files.getContentUri("external")
}
So now the code that works is the following
with(appContext.contentResolver) {
insert(externalUri, contentValues)?.let {
.....
However, i dont know the real reason that caused the error
Related
This Code Works Fine With Media Files I want a solution For Document Files
I Don't Know how to put contentValues For Document Files
fun getFile(fileName: String): File? {
with(sharePrefHelper.app){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val values = ContentValues()
// Here is My Question That what should i Do Here Because this is for document not for image
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
// for MIME_TYPE "image/jpg" this is working
values.put(MediaStore.Images.Media.MIME_TYPE, "text/csv")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/Donny")
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)?.let {
it.path?.let { finalPath ->
return File(finalPath)
}
}
} else {
val directory: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/Donny")
if (!directory.exists()){
directory.mkdirs()
}
return File(directory, fileName)
}
return null
}
}
This Code Works Fine with media Files
My Question Here is How to save documents like CSV File in outer folder of android device
EDIT :
Well well well, I'm still trying to add anytype of file in the "download" directory.
Personnaly, I'm trying to copy a file from my assetFolder and paste it to the "Download" folder. I haven't succeeded yet.
However, I can currently CREATE anytype of file in that folder, it's working with the method below. I hope this can help you.
Here is my code :
public void saveFileToPhone(InputStream inputStream, String filename) {
OutputStream outputStream;
Context myContext = requireContext();
try {
if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.Q){
ContentResolver contentResolver = requireContext().getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Downloads.DISPLAY_NAME,filename);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
Uri fileUri = contentResolver.insert(collection, contentValues);
outputStream = contentResolver.openOutputStream(Objects.requireNonNull(fileUri));
Objects.requireNonNull(outputStream);
}
}catch (FileNotFoundException e) {
e.printStackTrace();
}
}
Here it is what i have done with clean way
This is file provider activity
class FileProviderActivity : AppCompatActivity() {
var commonIntentLauncher: ActivityResultLauncher<Intent?> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.let {
intent.getParcelableExtra<ResultReceiver>("FileReceiver")?.send(0, bundleOf(
"FileUri" to result?.data?.data.toString()
))
finish()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
val activityIntent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = intent.getStringExtra("fileType")
putExtra(Intent.EXTRA_TITLE, intent.getStringExtra("fileName"))
putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI)
}
commonIntentLauncher.launch(activityIntent)
}else{
val directory: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS + "/Dunny/CSVFiles")
if (!directory.exists()){
directory.mkdirs()
}
intent.getParcelableExtra<ResultReceiver>("FileReceiver")?.send(0, bundleOf(
"FileUri" to File(directory, intent.getStringExtra("fileName")!!).toURI().toString()
))
finish()
}
}
}
This FileProviderHelper
class MyFileProvider {
companion object {
fun with(context: Context) = FileRequest(context)
}
class FileRequest(private val context: Context) {
fun request(fileName: String, fileType: String = "application/*", file: (Uri?) -> Unit ) {
val intent = Intent(context, FileProviderActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("fileName",fileName)
.putExtra("fileType", fileType)
.putExtras(bundleOf("FileReceiver" to FileReceiver(file)))
context.startActivity(intent)
}
}
internal class FileReceiver(private val file: (Uri?) -> Unit) : ResultReceiver(Handler(Looper.getMainLooper())) {
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
super.onReceiveResult(resultCode, resultData)
resultData?.let {
file(it.getString("FileUri")?.toUri())
}
}
}
}
Here Is Use Of this Function
MyFileProvider.with(this).request("TestFile.csv","application/*") { fileUri ->
toast(fileUri.toString())
}
I'm developing an applicaiton using Android 11 and Kotlin. I've successfully written a file to the Download folder on the device. When writing to the folder again using the same file name, the result is another file with the same name. So now I have two files with the same name.
So first I thought I'd just delete the file then write it again. I spent hours and hours trying that to no avail. The delete code would execute without exception but the file would never delete. I'm pretty sure I set the proper permissions by using
if (!isWriteExternalStoragePermissionGranted()) {
val permissions = arrayOf(WRITE_EXTERNAL_STORAGE)
for (i in permissions.indices) {
requestPermission(permissions[i], i)
}
}
private fun isWriteExternalStoragePermissionGranted(): Boolean {
val permissionCheck = ActivityCompat.checkSelfPermission(this, WRITE_EXTERNAL_STORAGE)
return permissionCheck == PackageManager.PERMISSION_GRANTED
}
Then I thought I'd truncate the contents of the file and just overwrite the files contents. That didn't work. Just annother copy of file again and again. I have spent almpost a full day on this. It really shouldn't be this hard. I've tried numerous examples.. nothing works. Here's my code to write the file...
fun writeToFile(applicationContext: Context, filename: String, data: String) {
try {
val resolver = applicationContext.contentResolver
val values = ContentValues()
values.put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
values.put(MediaStore.MediaColumns.MIME_TYPE, "text/xml")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
val uri = resolver.insert(MediaStore.Files.getContentUri("external"), values)
//val cr: ContentResolver = getContentResolver()
val os: OutputStream? = uri?.let { resolver.openOutputStream(it,"wt") }
if (os != null) {
os.write(data.toByteArray())
os.flush()
os.close()
}
/*
if (uri != null) {
resolver.openFileDescriptor(uri, "wt")?.use {
FileOutputStream(it.fileDescriptor).use {
it.write(data.toByteArray()
)
}
}
}
*/
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
Here's my code to delete the file first that never works. I've tried multiple variations...
fun deleteFile(context: Context, filename: String, applicationContext: Context){
val myFile = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), filename)
if (myFile.exists()) {
myFile.delete()
}
}
fun deleteFile(context: Context, filename: String, applicationContext: Context){
val resolver = applicationContext.contentResolver
val values = ContentValues()
values.put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
values.put(MediaStore.MediaColumns.MIME_TYPE, "text/xml")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
val uri = resolver.insert(MediaStore.Files.getContentUri("external"), values)
if (uri != null) {
resolver.delete(uri, null, null)
}
}
You should use the uri you got with the first insert() where you created the file.
I am working with Android version 10.
I have enabled Permissions to Read & Write Storage
Device Name : Poco F1
Scenario: I have to capture a screenshot of the current layout and save it to internalStorage and preview that image to the user. Here users have an option to delete the image.
Here are the codes I am using to save & delete
Saving a screenshot:
//I will pass the bitmap here
fun saveBitmapToInternalStorage(bitmap: Bitmap?) {
bitmap?.let {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
saveBitmapToOlderDevice(it)
} else {
saveBitmapToNewerDevice(it)
}
}
}
//This method is to save image to newerdevice >= Q
#RequiresApi(Build.VERSION_CODES.Q)
private fun saveBitmapToNewerDevice(bitmap: Bitmap) {
val uri = generateUri()
context.contentResolver.openOutputStream(uri ?: return).use { outputStream ->
outputStream?.let {
writeBitmapToJpeg(bitmap, outputStream, uri.toString())
}
}
}
//This is to generate the URI.
#RequiresApi(Build.VERSION_CODES.Q)
private fun generateUri(): Uri? {
val dateFormat = getDateFormat()
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "${dateFormat}.jpg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Pictures/${context.resources.getString(R.string.app_name)}")
}
return context.contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
}
// To save images to olderDevice
private fun saveBitmapToOlderDevice(bmp: Bitmap) {
val filename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)?.absolutePath +
"/${context.resources.getString(R.string.app_name)}/${getDateFormat()}.jpg"
createDirectory(filename)
val outputStream = FileOutputStream(filename)
writeBitmapToJpeg(bmp, outputStream, filename)
}
//This method is to save the image to InternalStorage
private fun writeBitmapToJpeg(bmp: Bitmap, outputStream: OutputStream, imagePath: String) {
try {
val outputData = ByteArrayOutputStream()
bmp.compress(CompressFormat.JPEG, 100, outputData)
outputData.writeTo(outputStream)
outputStream.flush()
outputStream.close()
} catch (e: IOException) {
showBitmapWriteErrorMessage()
}
}
I save the path while storing the image in internalStorgae
the path looks like
/storage/emulated/0/Pictures/TGP AR/20211011142001.jpg
and i pass this path into below method
To delete the image :
private fun deleteImage(imagePath: String) {
val file = File(imagePath)
file.delete()
}
file.exists() is returning true.
file.delete() is returning false.
I think, there might be two different ways to delete ( > & < Q ).
Please help me
You can delete the image by modifying your method to the following:
private fun deleteImage(imagePath: Uri) {
getContentResolver().delete(imagePath, null, null)
}
Then pass the Uri created in generateUri() to delete the file.
I am try to take photo in private folder and save to public media store.
val takePictureContract = registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) {
uri?.let { fileUri ->
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "pic_${System.currentTimeMillis()}")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
}
val mediaUri = contentResolver.insert(
MediaStore.Images.Media.INTERNAL_CONTENT_URI,
contentValues
)
mediaUri?.let { mUri ->
contentResolver.openOutputStream(mUri)?.use { os ->
contentResolver.openInputStream(fileUri)?.use { inputStream ->
inputStream.copyTo(os)
}
}
}
}
}
}
fun takePic() {
val currentTimeMillis = System.currentTimeMillis()
val folder = File(filesDir, "images")
val file = File(folder, "pic_${currentTimeMillis}")
folder.mkdirs()
uri = FileProvider.getUriForFile(this, "${packageName}.provider", file)
takePictureContract.launch(uri)
}
But after take photo, I have this error message. What's wrong with my code?
Error message I get is :
java.lang.UnsupportedOperationException: Writing to internal storage is not supported
If change INTERNAL_CONTENT_URI to EXTERNAL_CONTENT_URI, and require permission WRITE_EXTERNAL_STORAGE, I can save photo success.
My current Android Application stores pdf files on external storage using
val contentUri = MediaStore.Files.getContentUri(VOLUME_NAME_EXTERNAL)
The application creates a sub folder in the standard Documents folder.
My manifest contains
android:requestLegacyExternalStorage = true
For Android 30 I request the following
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
I have a background worker that attempts to clear all down loaded files, the code I employ is as follows:-
#RequiresApi(Build.VERSION_CODES.Q)
override suspend fun doActualFlowedWork(): Result {
if (hasSdkHigherThan(Build.VERSION_CODES.P)) {
clearDownloadFiles()
} else {
clearDownloadLegacyFiles()
}
return result ?: Result.success()
}
#Suppress("BlockingMethodInNonBlockingContext")
#RequiresApi(Build.VERSION_CODES.Q)
private fun clearDownloadFiles() {
val resolver = context.contentResolver
val relativeLocation = "${Environment.DIRECTORY_DOCUMENTS}${MY_SUB_FOLDER}"
val contentUri = MediaStore.Files.getContentUri(VOLUME_NAME_EXTERNAL)
resolver.query(
contentUri,
arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.RELATIVE_PATH),
"${MediaStore.MediaColumns.RELATIVE_PATH}=?",
arrayOf(relativeLocation),
null
).use { cursor ->
cursor?.let {
while (it.moveToNext()) {
val idIndex = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
val displayNameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
val relativePathNameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.RELATIVE_PATH)
if (cursor.getString(relativePathNameIndex) == relativeLocation) {
val fileContentUri = MediaStore.Files.getContentUri(VOLUME_NAME_EXTERNAL, cursor.getLong(idIndex))
val count = context.contentResolver.delete(fileContentUri, null, null)
if (count == 0) Timber.e("FAILED to clear downloaded file = ${cursor.getString(displayNameIndex)}")
else Timber.i("Cleared downloaded file = ${cursor.getString(displayNameIndex)}")
}
}
}
}
}
#Suppress("DEPRECATION")
private fun clearDownloadLegacyFiles() {
val documentsFolder = File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOCUMENTS)
if (!documentsFolder.exists()) return
val mendeleyLiteSubFolder = File(Environment.getExternalStorageDirectory(), "${Environment.DIRECTORY_DOCUMENTS}$MY_SUB_FOLDER")
if (!mendeleyLiteSubFolder.exists()) return
val downloadFiles = mendeleyLiteSubFolder.listFiles()
downloadFiles?.forEach { downloadFile ->
if (downloadFile.exists()) downloadFile.delete()
Timber.i("Clearing downloaded file = $downloadFile")
}
}
This clear down worker completes OK, with the logs showing the files have been deleted
however when I use Android Studio Device File Explorer to view my Document sub folder the physical pdf files are still present.
Are my expectations incorrect?
What does this code achieve context.contentResolver.delete(fileContentUri, null, null)?
How do I delete physical files from my sub folder?