I want to delete an image file from scoped storage
the images that are showing from the other directories.
I have successfully shown images but now I am unable to delete those images in android 11 the code for deleting images is working fine for android 10 or less.
private void delSysMedia(ImageModel mi) {
ContentResolver cr = context.getContentResolver();
cr.delete(Images.Media.EXTERNAL_CONTENT_URI, Images.Media._ID + "=?", new String[]{String.valueOf(mi.getId())});
cr.delete(Images.Thumbnails.EXTERNAL_CONTENT_URI, Images.Thumbnails.IMAGE_ID + "=?", new String[]{String.valueOf(mi.getId())});
}
here is the code that is being used by me in my image service class
For deleting files on Android 11 onwards you need MediaStore.createDeleteRequest and pass a list of Uri you want to delete it will show a system default chooser to the user asking to Allow or Deny the deletion of file.
You can use below code for deleting an Image file.
val uris = arrayListOf<Uri?>()
val uriOfCurrentFile= getImgUri(fileObject.absolutePath)
if (uriOfCurrentFile!= null) {
uris.add(uriOfCurrentFile)
}
val intentSenderLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if (it.resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Toast.makeText(context, "Deleted Successfully", Toast.LENGTH_SHORT).show()
}
}
}
//This function gets Uri of file for deletion
fun getImgUri(
path: String,
): Uri? {
try {
val checkFile = File(path)
Timber.e("checkDelete- $checkFile")
if (checkFile.exists()) {
var id: Long = 0
val cr: ContentResolver = activity?.contentResolver!!
val selection = MediaStore.Images.Media.DATA
val selectionArgs = arrayOf<String>(checkFile.absolutePath)
val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA)
val sortOrder = MediaStore.Images.Media.TITLE + " ASC"
val cursor = cr.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
"$selection=?", selectionArgs, null
)
if (cursor != null) {
while (cursor.moveToNext()) {
val idIndex = cursor.getColumnIndex(MediaStore.Images.Media._ID)
id = cursor.getString(idIndex).toLong()
Timber.e("checkFileID- $id")
try {
val photoUri: Uri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
return photoUri
} catch (securityException: SecurityException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val recoverableSecurityException =
securityException as? RecoverableSecurityException
?: throw securityException
recoverableSecurityException.userAction.actionIntent.intentSender
} else {
throw securityException
}
}
}
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
return null
}
//now you have list of Uri you want to delete
if (uris != null && uris.size > 0) {
var intentSender = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
MediaStore.createDeleteRequest(
activity!!.contentResolver,
uris
).intentSender
}
else -> null
}
intentSender?.let { sender ->
intentSenderLauncher.launch(
IntentSenderRequest.Builder(sender).build()
)
}
}
Please note if not an Image File, you will have to replace
MediaStore.Images with MediaStore.Video, MediaStore.Audio, MediaStore.Files etc.
Related
I added the request premissions for reading files tried this method inside my activity but nothing shows,like there is no images in real devices and emulator,what am i doing wrong?
fun storaheread() {
val imageProjection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media._ID
)
val imageSortOrder = "${MediaStore.Images.Media.DATE_TAKEN} DESC"
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
imageProjection,
null,
null,
imageSortOrder
)
cursor.use {
it?.let {
val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val nameColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)
val dateColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN)
while (it.moveToNext()) {
val id = it.getLong(idColumn)
val name = it.getString(nameColumn)
val size = it.getString(sizeColumn)
val date = it.getString(dateColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
// add the URI to the list
// generate the thumbnail
// val thumbnail = (this as Context).contentResolver.loadThumbnail(contentUri, Size(480, 480), null)
Log.d("image name",name)
}
} ?: kotlin.run {
Log.e("TAG", "Cursor is null!")
}
}
}
you can check out this similar question, it may help you achieve this situation.
Loading all the images from gallery into the Application in android
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?
I have a fun that saves bitmap as PNG or JPG (both not working), but seems like using content values not working as expected.
File name is incorrect.
File type is incorrect.
What am I missing ?
Works on Android 10, but not working on Android 8
fun Bitmap.save(context: Context) {
val contentResolver = context.contentResolver
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "test.png")
put(MediaStore.MediaColumns.TITLE, "test")
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
put(MediaStore.MediaColumns.IS_PENDING, 1)
}
}
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val uri = contentResolver.insert(contentUri, contentValues)
if (uri != null) {
try {
contentResolver.openFileDescriptor(uri, "w", null)?.use {
if (it.fileDescriptor != null) {
with(FileOutputStream(it.fileDescriptor)) {
compress(
Bitmap.CompressFormat.PNG,
DEFAULT_IMAGE_QUALITY,
this
)
flush()
close()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.clear()
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
contentResolver.update(uri, contentValues, null, null)
}
MediaScannerConnection.scanFile(context, arrayOf(uri.toString()), null, null)
}
recycle()
}
Actual file name is 1592205828045 (some timestamp)
Actual file type is jpg with 0B - as it was not saved properly ?
You will have to maintain 2 different ways of saving images to shared storage. This post covers it quite well. Using Media Store API in older phones results in the problem you have described. Some code sample for you (tested in Android 8, 10, and 11).
Add these to your manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- File save functions handles this -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
tools:ignore="ScopedStorage" />
Add a permission check to your app (code not provided)
When you are ready with your bitmap call either of these functions (depending on the SDK version of the phone that the app is currently running on)
//TODO - bitmap needs null check
val bitmap = BitmapFactory.decodeFile(bitmapFile.canonicalPath)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
{
saveBitmapPreQ(bitmap)
} else {
saveBitmapPostQ(bitmap)
}
Finally these are the implementations of saveBitmapPreQ and saveBitmapPostQ
#Suppress("DEPRECATION") // Check is preformed on function call
private fun saveBitmapPreQ(thisBitmap: Bitmap){
Log.d("HOME_4", "in pre Q")
val pictureDirectory =
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"MyFolder")
if (!pictureDirectory.exists()){
pictureDirectory.mkdir()
}
val dateTimeStamp = SimpleDateFormat("yyyyMMddHHmmss").format(Date())
val name = "Image_$dateTimeStamp"
val bitmapFile = File(pictureDirectory, "$name.png")
try {
val fileOutputStream = bitmapFile.outputStream()
thisBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
fileOutputStream.flush()
fileOutputStream.close()
} catch (e: Exception) {
Log.d("HOME_5", "Pre Q error $e")
}
}
private fun saveBitmapPostQ(thisBitmap: Bitmap){
Log.d("HOME_6", "in post Q")
val dateTimeStamp = SimpleDateFormat("yyyyMMddHHmmss").format(Date())
val name = "Image_$dateTimeStamp"
val relativePath = Environment.DIRECTORY_PICTURES + File.separator + "MyFolder"
val contentValues = ContentValues().apply {
put(MediaStore.Images.ImageColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
put(MediaStore.MediaColumns.TITLE, name)
put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativePath)
}
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
var outputStream: OutputStream? = null
var uri: Uri? = null
try {
uri = contentResolver.insert(contentUri, contentValues)
if (uri == null){
throw IOException("Failed to create new MediaStore record.")
}
outputStream = contentResolver.openOutputStream(uri)
if (outputStream == null){
throw IOException("Failed to get output stream.")
}
if (!thisBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)){
throw IOException("Failed to save bitmap.")
}
} catch (e: IOException){
if (uri != null)
{
contentResolver.delete(uri, null, null)
}
throw IOException(e)
}
finally {
outputStream?.close()
}
}
I have left log messages in there to help you understand the flow. In the saveBitmapPostQ funtions I have taken a few shortcuts. Please read this post under the headding Creating a New File on how you can improve that function further.
You are creating the file, but you still need to write your Bitmap to it:
fun Bitmap.save(context: Context) {
...
val bitmap = this
val maxImageQuality = 100
val uri = contentResolver.insert(contentUri, contentValues)
if (uri != null) {
try {
contentResolver.openFileDescriptor(uri, "w", null)?.use {
if (it.fileDescriptor != null) {
with(FileOutputStream(it.fileDescriptor)) {
bitmap.compress(
Bitmap.CompressFormat.PNG,
maxImageQuality, this
)
flush()
close()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
// release pending status of the file
contentValues.clear()
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0)
contentResolver.update(uri, contentValues, null, null)
// notify media scanner there's a new picture
MediaScannerConnection.scanFile(context, arrayOf(uri.toString()), null, null)
}
// don't forget to recycle the bitmap when you don't need it any longer
bitmap.recycle()
}
I am trying to attach documents from download folder. But i am getting the error
"java.io.FileNotFoundException: /storage/emulated/0/DownloadReact-native commands.pdf (No such file or directory)".Here i show the code that i used to get path.
if (isDownloadsDocument(uri)) {
Log.d("build_version_one", Build.VERSION.SDK_INT.toString())
Log.d("build_version_two", Build.VERSION_CODES.M.toString())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val id: String
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), null, null, null)
if (cursor != null && cursor.moveToFirst()) {
val fileName = cursor.getString(0)
// val path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName
val path = Environment.getExternalStorageDirectory().toString() +"/Download" + fileName
val ss = Environment.getExternalStoragePublicDirectory("").toString()+"/Download" + fileName
Log.d("orginal_path","one"+path)
Log.d("orginal_path","two"+ss)
if (!TextUtils.isEmpty(path)) {
Log.d("orginal_path","working")
return path
}
}
} finally {
cursor?.close()
}
id = DocumentsContract.getDocumentId(uri)
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:".toRegex(), "")
}
val contentUriPrefixesToTry = arrayOf(
"content://downloads/public_downloads",
"content://downloads/my_downloads"
)
for (contentUriPrefix in contentUriPrefixesToTry) {
return try {
val contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), java.lang.Long.valueOf(id))
Log.d("orginal_content_uri", contentUri.toString())
/* final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));*/
getDataColumn(context, contentUri, null, null)
} catch (e: NumberFormatException) {
//In Android 8 and Android P the id is not a number
uri.path.replaceFirst("^/document/raw:", "").replaceFirst("^raw:", "")
}
}
}
}
}
As the Exception states, there is no such file/directory available on your disk or path provided is incorrect.
At the first glance, it seems you missed a slash between filename and Download directory /storage/emulated/0/DownloadReact-native commands.pdf. Make sure you have a slash between filename and a path.
val path = Environment.getExternalStorageDirectory().toString() +"/Download/" + fileName
val ss = Environment.getExternalStoragePublicDirectory("").toString()+"/Download/" + fileName
See if it works. Otherwise verify if you file in the respective directory.
getExternalStorageDirectory deprecated in API level 29 java. Use getExternalFilesDir(), getExternalCacheDir(), or getExternalMediaDir() (methods on Context).
I want to change ringtone of given phone number programmatically.
I have facing below mention issues.
1. Ringtone is not change on some Nougat device (LG k10, Samsung).
2. It is working fine below the Nougat device.
I am using below mention code to change the ringtone of phone number.
private fun funChangeRingtone(number: String) {
try {
// The Uri used to look up a contact by phone number
val lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number)
// The columns used for `Contacts.getLookupUri`
val projection = arrayOf<String>(Contacts._ID, Contacts.LOOKUP_KEY)
// Build your Cursor
val data = contentResolver.query(lookupUri, projection, null, null, null)
data!!.moveToFirst()
try {
// Get the contact lookup Uri
val contactId = data.getLong(0)
val lookupKey = data.getString(1)
val contactUri = Contacts.getLookupUri(contactId, lookupKey) ?: // Invalid arguments
return
val file = File(/*storage + "/AudioRecorder",*/ audio /*+ ".mp3"*/)
file.setReadable(true, false);
var value: Uri
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
value = FileProvider.getUriForFile(this, null, file);
grantUriPermission("com.example", value, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {*/
value = Uri.fromFile(file)/*.toString()*/
// }
// Apply the custom ringtone
val values = ContentValues()
values.put(Contacts.CUSTOM_RINGTONE, value.toString())
var permission = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
permission = Settings.System.canWrite(this)
} else {
permission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED
}
if (permission) {
val update = contentResolver.update(contactUri, values, null, null)
Log.d("update", update.toString())
}
} finally {
// Don't forget to close your Cursor
data.close()
}
} catch (e: Exception) {
e.printStackTrace()
}
}