How to refresh Android's MediaStore upon photo deletion
Asked Answered
A

4

7

Question: how to make the media store to refresh its entry of a DELETED file?

After deleting a photo in code from the external storage, I still see a slot for the deleted photo in the gallery - blank photo.

It seems that the gallery reflects the media store and the deleted photo is found in the media store until the phone is restarted or generally - until the media is rescanned.

Trying to scan the deleted file did not help scanning deleted files (works just for new or existing files): MediaScannerConnection.scanFile(Application.get(), new String[]{file.getPath()}, null, null) (I tried scanning the parent folder as well).

Also tried ACTION_MEDIA_SCANNER_SCAN_FILE to no avail. Example: Application.get().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)))

Sending a broadcast receiver to rescan the entire external storage (thus refreshing the media store)did the trick: Application.get().sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(Environment.getExternalStorageDirectory())))

BUT, it seems that Android, as of 4.4, throws a security exception when trying to manually send the ACTION_MEDIA_MOUNTED system broadcast. See @CommonsWare's post: http://commonsware.com/blog/2013/11/06/android-4p4-permission-regressions.html

So, I'm stuck with no solution for refreshing the media store upon file(/photo/video/etc.) deletion.

Axe answered 13/11, 2013 at 13:50 Comment(0)
P
4

I found the following that works for me in 4.4 on a Nexus 10.

// request scan
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, SELECT_PICTURE);
Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
scanIntent.setData(Uri.fromFile(refreshFile));
sendBroadcast(scanIntent);

"refreshFile" is the file I deleted that I get from my String "fPath" and then convert it to a file.

String filePath = fPath;        
File refreshFile = new File(filePath);
Photomontage answered 23/11, 2013 at 15:15 Comment(5)
What's the purpose of 'ACTION_GET_CONTENT'?Axe
As I said... I found this. I am new to all this and do not claim to be an expert. I just know it worked for me.Photomontage
The code you wrote also starts an activity with the ACTION_GET_CONTENT action. If it's part of the solution, it's problematic by itself.Axe
I tried ACTION_MEDIA_SCANNER_SCAN_FILE but didn't work (on a SGS4 anyhow).Axe
Kilaka, This is NOT My Code. as I SAID. I FOUND IT!! and it works for ME!! So you bring my reputation down because it doesn't work for you... So much for trying to help people.Photomontage
I
1

I had the same issue. I wrote the following code and it worked on all versions from lollipop to oreo. I also called the mediastore.scanfile() method to ensure that MediaStore is updated. Adding the code below - you might not want to use the "delete()" method in future as the scanfile() might be comprehensive. But, if you want to support older phones then delete() would probably be safer.

    // fileID == MediaStore.Images.Media._ID; for the file when you get the file from the content 
// resolver
public static boolean deleteCREntryForFilePath(Context context, String filePath, long fileID) {
    boolean         fDeleted       = false;
    ContentResolver cr             = context.getContentResolver();
    int             rowsDeleted    = 0;
    Uri             imageURI       = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    String          deleteStr      = MediaStore.Images.Media._ID + "=" + fileID;

    MediaScannerConnection.scanFile(context, new String[]{filePath}, null, null);

    // Remove entry from database
    rowsDeleted = context.getContentResolver().delete(
            imageURI, deleteStr, null);
    if (rowsDeleted > 0)
        fDeleted = true;
    return(fDeleted);
}

Here is the code to get the file-id (name of the function is getfileId()) . It works for different file-types. You cannot compile the code as it is because it uses an internal object-type but you should be easily able to convert this for generic use.

public static String[] getCombinedEntityColumns(Constants.DELASHARE_OBJECT_TYPES objType) {
    String[] entityColumns = new String[5];

    switch (objType) {
        case DELASHARE_OBJECT_PICTURE:
        case DELASHARE_OBJECT_MUSIC:
        case DELASHARE_OBJECT_VIDEO: {

            entityColumns[0] = MediaStore.Images.Media.DISPLAY_NAME;
            entityColumns[1] = MediaStore.Images.Media.DATA;
            entityColumns[2] = MediaStore.Images.Media._ID;
            entityColumns[3] = MediaStore.Images.Media.DATE_ADDED;
            //entityColumns[3] = MediaStore.Images.Media.DATE_TAKEN;
            entityColumns[4] = MediaStore.Images.Media.SIZE;
            break;
        }
        case DELASHARE_OBJECT_APK:
        case DELASHARE_OBJECT_DOCUMENT:
        case DELASHARE_OBJECT_DOWNLOAD:
        case DELASHARE_OBJECT_SEARCH_RESULTS:
        default: {
            entityColumns[0] = MediaStore.Files.FileColumns.DISPLAY_NAME;
            entityColumns[1] = MediaStore.Files.FileColumns.DATA;
            entityColumns[2] = MediaStore.Files.FileColumns._ID;
            entityColumns[3] = MediaStore.Files.FileColumns.DATE_MODIFIED;
            entityColumns[4] = MediaStore.Files.FileColumns.SIZE;
            break;
        }
    }
    return (entityColumns);
}

public static Uri getCategoryUri(Constants.DELASHARE_OBJECT_TYPES categoryObjType) {
    Uri  objUri = null;

    switch(categoryObjType) {
        case DELASHARE_OBJECT_PICTURE:
            objUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            break;
        case DELASHARE_OBJECT_VIDEO:
            objUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            break;
        case DELASHARE_OBJECT_MUSIC:
            objUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            break;
        case DELASHARE_OBJECT_DOWNLOAD: {
            File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
            objUri = Uri.fromFile( downloadDir);
            //objUri = MediaStore.Files.getContentUri("external");
            break;
        }
        case DELASHARE_OBJECT_APK:
        case DELASHARE_OBJECT_DOCUMENT:
        case DELASHARE_OBJECT_SEARCH_RESULTS:
        default:
            objUri = MediaStore.Files.getContentUri("external");
            break;

    }

    return(objUri);
}

public  static long getFileId(Context context, String dirPath, String filePath, String fileName, Constants.DELASHARE_OBJECT_TYPES objType) {
    boolean fIDFound = false;
    long    id       = 0;
    if (!fIDFound) {
        String          sortOrder      = null;
        String[]        entityColumns  = getCombinedEntityColumns(objType);
        Uri             categoryUri    = getCategoryUri(objType);
        String          selection      = null;
        String[]        selectionArgs  = new String[]{Constants.DELA_PERCENT_STR + dirPath};
        ContentResolver cr             = context.getContentResolver();
        Cursor cursor = null;

        switch (objType) {
            case DELASHARE_OBJECT_PICTURE:
                selection = MediaStore.Images.Media.DATA + " LIKE ?";
                break;
            case DELASHARE_OBJECT_VIDEO:
                selection = MediaStore.Video.Media.DATA + " LIKE ?";
                break;
            case DELASHARE_OBJECT_DOCUMENT:
            default:
                selection = MediaStore.Files.FileColumns.DATA + " LIKE  ?";
                break;
        }
        cursor = cr.query(
                categoryUri,
                entityColumns,
                selection,
                selectionArgs,
                sortOrder);

        if (cursor != null && cursor.moveToFirst()) {
            id = cursor.getLong(cursor.getColumnIndex(entityColumns[2]));

            if (id != 0) {
                fIDFound = true;
            }
        }
        if (cursor != null) {
            cursor.close();
            cursor = null;
        }
    }
    return(id);

}
Instruct answered 15/1, 2019 at 10:59 Comment(4)
and how we get file id?Intisar
I have pasted the code to get the file id above. It is quite involved and it works for all kinds of files. Hope this helps.Instruct
there is no such method MediaStore.scanfile() Adcock
The API to use is MediaScannerConnection.scanFile() You can look at the following site for workings of this API. developer.android.com/reference/android/media/…Instruct
W
0

I had the same question as you now that the sendBroadcast approach is disallowed in 4.4 and found a good solution here using the Media Store content provider: https://mcmap.net/q/146512/-android-deleting-an-image

I tested it out on Android 4.4 and it works nicely. I think it is a solid approach.

Wolter answered 12/2, 2014 at 17:9 Comment(0)
N
0

Try

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));

Add this to your manifest:

<intent-filter>
      <action android:name="android.intent.action.MEDIA_MOUNTED" />
      <data android:scheme="file" /> 
</intent-filter>
Noise answered 21/9, 2016 at 10:56 Comment(1)
it's maximum for API 18 (Jelly Bean), how could you answer it in 2016? it's quite old API, on higher API you will get exception, now only system apps can do it. Also what <intent-filter> means here, it isn't needed and it does nothingAdcock

© 2022 - 2024 — McMap. All rights reserved.