13

My app allows a user to save an image to their SD card. But I'm not sure how to make it appear in the gallery until you unmount and remount the SD card. I have googled for a couple of days with this problem but am not sure how to make it appear automatically. I found this link but I'm not sure how to use the class. This is what i use to save the file. At the bottom of the try catch block is where I want to scan the sd card for new media.

    FileOutputStream outStream = null;
    File file = new File(dirPath, fileName);
    try {
        outStream = new FileOutputStream(file);
        bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
        outStream.flush();
        outStream.close();
    } catch {
         ...
    }

If anyone could point me in the right direction, I would appreciate.

Brian
  • 819
  • 4
  • 20
  • 35
  • The "sdrescan" app does what you want. It's free; maybe you could ask the author for a code snippet. Or just simply launch it from your app. – Edward Falk Jan 20 '11 at 22:31
  • Is there a way to rescan without stopping the music player? It says Sorry, the media player doesn't support this file, until I press back a track, forward a track, then it plays again. – NoBugs May 11 '12 at 16:43

6 Answers6

10

I've tried plenty of different methods to trigger the MediaScanner, and these are my results.

SendBroadcast

The most simple and naive solution. It consists in executing the following instruction from your code:

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

However, this no longer works in KitKat devices, due to a lack of required permissions.


MediaScannerWrapper

As posted here (per @Brian's answer), it consists in wrapping a MediaScannerConnection instance in order to trigger the scan() method over a specific directory. This method has proven to be working fine for 4.3 and below, but still no luck with KitKat (4.4+).


FileWalker

One of the many Play Store apps that tries to overcome the MediaStore's lack of commitment to update its database is ReScan SD. It sends a lot of different broadcasts:

  sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri.parse("file://" + Environment.getExternalStorageDirectory())));
  sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri.parse("file:///Removable")));
  sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri.parse("file:///Removable/SD")));
  sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri.parse("file:///Removable/MicroSD")));
  sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri.parse("file:///mnt/Removable/MicroSD")));
  sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri.parse("file:///mnt")));
  sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri.parse("file:///storage")));
  sendBroadcast(new Intent("android.intent.action.MEDIA_MOUNTED", Uri.parse("file:///Removable")));

and tries to support KitKat by manually triggering the scan() method over each file of the base directory. Unfortunately, this is both very CPU-intensive and time-consuming, so it is not very recommended.


"The shell way"

The only thing that seem to work with KitKat in some cases is sending the broadcast via adb shell. So, this snippet allows you to do just that programmatically:

Runtime.getRuntime().exec("am broadcast -a android.intent.action.MEDIA_MOUNTED -d file://" + Environment.getExternalStorageDirectory());

It is more of an hack-ish way of doing it, but at the moment is the best I could come up with.


Bottom line

Each of the above solutions actually works for everything that is not KitKat. That's because, thanks to Justin, a bug has been found and issued to the official Tracker. This means that, until the bug is ironed out, we are left with no true KitKat support.

Which one to use? Among those, I would use the MediaScannerWrapper solution, together with the shell-ish approach (the last one).

Community
  • 1
  • 1
Sebastiano
  • 12,289
  • 6
  • 47
  • 80
  • The shell method on KITKAT You mentioned it works in some cases. I am keen to know underwhich use case this method fails ? or under which usecases it works ? – Ahmed Apr 15 '14 at 15:51
  • I couldn't find any fixed use case. The only thing that seems to work is refreshing the MediaScanner after a file has been added. On the other hand, it does not work when a file is removed. – Sebastiano Apr 15 '14 at 16:01
  • 2
    It worked when I sent broadcast from the command line using adb shell works for new files as well as deleted ones. But it does not work programatically on KitKat either for new files or removed one. I was testing with audio music files and using emulator API 19. – Ahmed Apr 16 '14 at 00:27
  • I think this can be done. because when I rename or add some media file, then using the ES File Explorer and changing the path of that media containing folder, they get appear in gallery and added to media store, It takes some time to regenerate thumbnail if you have to much media on that folder. by the way I watch the logcat when I add or rename an image file: `Media Provider object removed` then some logcat with tag `art` about crating bitmap, I'm sure the only way to do this is updating MediaStore with new data and bitmap and if you rename or move medias remove the last and add the new one. – M. Reza Nasirloo Jul 04 '14 at 08:58
5

Since the last answer I posted apparently wasn't an appropriate method, I found another method here. You basically create a wrapper class, initialize it, and then call the scan() method. Very helpful post. Let me know if this isn't appropriate either.

Community
  • 1
  • 1
Brian
  • 819
  • 4
  • 20
  • 35
  • Note: This uses the scanFile() that has been available from the beginning API Level 1. Not the newer scanFile() that was made available after API Level 8. – Brian Jan 24 '11 at 22:42
3

You could also call media scanner explicitly by sending broadcast.

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

Edit

This was an old post. Updating it to new versions

Android is taking steps to prevent apps from spoofing more system broadcasts like this.

If you want to tell Android to index a file you put on external storage, either use MediaScannerConnection or ACTION_MEDIA_SCANNER_SCAN_FILE

Reference: This post

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    final Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    final Uri contentUri = Uri.fromFile(outputFile); 
    scanIntent.setData(contentUri);
    sendBroadcast(scanIntent);
} else {
    final Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()));
    sendBroadcast(intent);
}

If the above piece of code is not working you can try the following:

MediaScannerConnection.scanFile(this, new String[] {
    file.getAbsolutePath()
}, null, new MediaScannerConnection.OnScanCompletedListener() {

    @Override
    public void onScanCompleted(String path, Uri uri) {


    }
});
SKT
  • 1,821
  • 1
  • 20
  • 32
3

Use MediaScannerConnection:

http://developer.android.com/reference/android/media/MediaScannerConnection.html

It can be a little bit of a pain because of the multiple levels of asynchronous calls, so as of API 8 (Froyo) there is a helper function:

http://developer.android.com/reference/android/media/MediaScannerConnection.html#scanFile(android.content.Context, java.lang.String[], java.lang.String[], android.media.MediaScannerConnection.OnScanCompletedListener)

hackbod
  • 90,665
  • 16
  • 140
  • 154
  • I have read through the reference several times and I can't figure out how to get mediaScannerConnection to work. As far as the helper function, I don't think that would be the best approach yet. API 8 isn't very old and that would not be supported on a lot of devices (including my test phone). I played around for a couple of days with the link in my initial post and I couldn't get that example to work. If you could post some snippets or point me to another example, I would appreciate it. Until then I will have to leave the "sendBroadcast" method in since its the only way that works now. – Brian Jan 23 '11 at 04:30
  • There is an API demo for the new function here: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.html You can find the code implementation that function to model for your own implementation here: http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=media/java/android/media/MediaScannerConnection.java – hackbod Jan 24 '11 at 03:25
  • 1
    To re-iterate: DO NOT SEND ACTION_MEDIA_MOUNTED. That will do ALL KINDS of stuff, including a full scan of the SD card. In fact I need to make sure that in future versions of the platform apps can't send it, because it is likely to cause parts of the platform to misbehave if they get it at the wrong time. So expect in the future if you are doing this for your app to break. – hackbod Jan 24 '11 at 03:26
  • 2
    Like I said, I want to change it and do it the right way. But I can't get the "right way" to work. If you could help out by telling me what to do instead of telling me what not to do, I would appreciate it. – Brian Jan 24 '11 at 19:11
  • @hackbod - the only way I found to get the mediastore to scan a **deleted** photo was with ACTION_MEDIA_MOUNTED. I tried several solutions and all failed: MediaScannerConnection.scanFile(...) with file and parent. ACTION_MEDIA_SCANNER_SCAN_FILE didn't work as well. – AlikElzin-kilaka Nov 13 '13 at 13:20
  • @hackbod - added a new SO question: http://stackoverflow.com/questions/19955546/how-to-refresh-androids-mediastore-upon-photo-deletion – AlikElzin-kilaka Nov 13 '13 at 13:51
1

Here is another way to force scan:

context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,"uri to file"));

And then system will fire ACTION_MEDIA_SCANNER_FINISHED broadcast so you can react on it with BroadcastReceiver

In order to be able to receive ACTION_MEDIA_SCANNER_FINISHED broadcast, intent filter should contain data scheme:

IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_FINISHED);
intentFilter.addDataScheme("file");
context.registerReceiver(mMediaScannerFinishReceiver, intentFilter);

from the doc:

public static final String ACTION_MEDIA_SCANNER_SCAN_FILE

Added in API level 1 Broadcast Action: Request the media scanner to scan a file and add it to the media database. The path to the file is contained in the Intent.mData field.

and

public static final String ACTION_MEDIA_SCANNER_FINISHED

Added in API level 1 Broadcast Action: The media scanner has finished scanning a directory. The path to the scanned directory is contained in the Intent.mData field.

vir us
  • 9,920
  • 6
  • 57
  • 66
0

You can use MediaStore.Images.Media.insertImage to insert an item into the gallery.

jjb
  • 3,560
  • 1
  • 21
  • 30
  • Thanks for the help. But I couldn't get "insertImage" to work. I found the answer on another forum though. – Brian Jan 20 '11 at 22:48