6

Because I want to make sure the MediaStore has the latest information without having to reboot I'd like to trigger the MediaScanner using the popular way I found on SO

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

This works fine on my Samsung S2 w/ICS but not on my Nexus 7 w/JellyBean. Logcat shows this on my Nexus 7:

WARN/ActivityManager(480): Permission denied: checkComponentPermission() owningUid=10014
WARN/BroadcastQueue(480): Permission Denial: broadcasting Intent { act=android.intent.action.MEDIA_MOUNTED dat=file:///storage/emulated/0 flg=0x10 } from com.example.foo.bar (pid=17488, uid=10046) is not exported from uid 10014 due to receiver com.android.providers.downloads/.DownloadReceiver
INFO/ActivityManager(480): Start proc com.google.android.music:main for broadcast com.google.android.music/.store.MediaStoreImportService$Receiver: pid=17858 uid=10038 gids={50038, 3003, 1015, 1028}
INFO/MusicStore(17858): Database version: 50
INFO/MediaStoreImporter(17858): Update: incremental Added music: 0 Updated music: 0 Deleted music: 0 Created playlists: 0 Updated playlists: 0 Deleted playlists: 0 Inserted playlist items: 0 Deleted playlist items: 0 Removed orphaned playlist items: 0

The last line sounds encouraging in theory, but the values are always 0 even after new files had been pushed to the SD card (via adb push). On my older device (S2) it does remount the SD card.

I've added the following permissions to my AndroidManifest.xml but it behaves the same as without those permissions:

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Any ideas/alternatives?


Edit 1:

Note that I don't know any file paths of new or modified or deleted files. I just want to make sure the MediaStore is up-to-date.

Bernd S
  • 1,278
  • 1
  • 11
  • 18
  • does this have anything to do with [this issue](http://stackoverflow.com/questions/14346160/saving-files-on-external-storage-on-nexus-7-and-retrieving-from-pc) issue? – Daniel Smith Feb 28 '13 at 23:23
  • That's quite possible. Thanks for the link. I'm going to see if I can get access to other devices to see if it's only Nexus 4/7. – Bernd S Mar 01 '13 at 01:24
  • I haven't had any issue on Nexus 4 using [this function for triggering a mediascan](https://gist.github.com/danoz73/5061888) – Daniel Smith Mar 01 '13 at 01:54
  • This is weird. I tried it on my Nexus 7 again, and the MediaStore does get updated after I use above method, even though I get the permission denied message in logcat. FYI, I don't need those permissions in the manifest. – Bernd S Mar 02 '13 at 18:52
  • just for clarity -- did my method work? or did your method work? – Daniel Smith Mar 02 '13 at 20:56
  • @DanielSmith: Just noticed you use a different action, `ACTION_MEDIA_SCANNER_SCAN_FILE`, in your gist. This doesn't work when passing in `Environment.getExternalStorageDirectory()` or even the directory where the new file resides. If you know the file path to the new files then CommonsWare's solution is the way to go. I do like the idea of using `Uri.fromFile()` instead of `Uri.parse("file://" + file)`. – Bernd S Mar 03 '13 at 00:08
  • I still can't get `Intent.ACTION_MEDIA_MOUNTED` to work on my Nexus 4. I get the same logcat messages but new media still doesn't get scanned in. Haven't tested on a Nexus 7 though. @DanielSmith I've tested your method and it does seem to work. I've previously run into issues (probably edge cases) from users complaining that CommonsWare's method sometimes still doesn't scan. Very hard to reproduce though, but I think I'll switch over and see if it alleviates the problem. – Tony Chan Apr 23 '13 at 23:11

3 Answers3

12

Here's the sample code based on CommonsWare's answer:

MediaScannerConnection.scanFile(activity, new String[]{path}, null,
                                new MediaScannerConnection.OnScanCompletedListener() {
    @Override
    public void onScanCompleted(final String path, final Uri uri) {
        Log.i(TAG, String.format("Scanned path %s -> URI = %s", path, uri.toString()));
    }
});

Even though in most of the cases, where one knows the files to be added/updated/etc. to the MediaStore, one should follow CommonsWare's answer, I wanted to post the my solution where I need to do it the rough way because I don't know the file paths. I use this mostly for testing/demoing:

Uri uri = Uri.fromFile(Environment.getExternalStorageDirectory());
activity.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, uri));

BTW, no permissions are necessary for either solution.

Bernd S
  • 1,278
  • 1
  • 11
  • 18
  • 11
    It appears that your `ACTION_MEDIA_MOUNTED` approach is now blocked for non-system apps on Android 4.4. – CommonsWare Nov 04 '13 at 18:57
  • 1
    Yup and apps doing it will crash with "not allowed to send broadcast android.intent.action.MEDIA_MOUNTED" :/ – markostamcar Nov 07 '13 at 19:00
  • for me triggering `MediaScannerConnection.scanFile()` unfortunately causes the file to appear in gallery, but its content is not visible. Sending a broadcast either takes very long time to be processed, or does not work at all for my case. (my test devices are Android 4.4/5.0) – Antek Jul 27 '17 at 13:17
8

using the popular way I found on SO

Faking ACTION_MEDIA_MOUNTED broadcasts has never been an appropriate solution IMHO.

Any ideas/alternatives?

Use MediaScannerConnection, such as via its scanFile() static method.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Good point! After some experimentation it only seems to work when I already know which file was added, i.e. if I provide the full path to the file including file name. In my case I don't know that. If a directory is provided it only tries to add that directory to the MediaStore, but not any files or directories underneath. – Bernd S Mar 02 '13 at 07:31
  • @BerndS: "In my case I don't know that" -- um, then why are you messing with the `MediaStore`? – CommonsWare Mar 02 '13 at 16:13
  • Because it's a easy way to get information about all media files on the device. It's also faster than to crawl the file system myself. – Bernd S Mar 02 '13 at 18:36
  • @BerndS: No, I understand that. But there is no need for you to do `ACTION_MEDIA_MOUNTED` *or* `scanFile()` **unless you have something needing to be scanned**. – CommonsWare Mar 03 '13 at 00:05
  • 1
    Agreed. I'm not 100% sure if this requirement makes it into the final app. Just thought it would be nice to have when demoing or testing the app. I'd `adb push` some new files to the SD card and then the app is supposed to do it's thing with fresh files. It just got very tiring having to reboot the device or emulator every time. – Bernd S Mar 03 '13 at 00:57
  • I'm accepting this answer because in the majority of the cases this is what one should do IMHO. – Bernd S Mar 05 '13 at 22:39
  • @CommonsWare, is there any reason you would prefer your solution over using `context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri))`? I've seen both as methods of scanning a specific media file, but I've occasionally gotten complaints from users that pictures don't show up in the gallery using your proposed method. I'm not sure if the broadcast method has any quirks as well though. – Tony Chan Apr 23 '13 at 23:07
  • @Turbo: Your `Intent` action should be fine (note that it is not the one from the OP). Personally I haven't used it. – CommonsWare Apr 23 '13 at 23:13
  • @CommonsWare good to know, thanks! Yeah, I only brought up this `Intent` because someone else mentioned it to the OP as well, and I'm searching for an alternative to `MediaScannerConnection`. Hopefully this method won't give me any scanning problems. – Tony Chan Apr 24 '13 at 23:57
  • @CommonsWare thank you for the answer but I am still not seeing my list update after using this code. I copied from the API Demos and am using `File file = getExternalFilesDir(Environment.DIRECTORY_MUSIC);` before your code to assign the 'file' value. I have an activity that populates a ListView on opening and if I delete a song, even after reopening the app, the list is not updated unless I restart the device (Nexus 7 w/ 4.4) Is this correct? It was working on pre-4.4 devices. Thank you very much for any help. – midiwriter Nov 25 '13 at 22:20
  • 1
    @MikeLudwig: I am not aware that `scanFile()` or any other solution is recursive. If you delete a file, try scanning the old file path: http://stackoverflow.com/a/14849609/115145 – CommonsWare Nov 25 '13 at 22:31
  • I was having an issue today where any file placed in getExternalFilesDir(Environment.DIRECTORY_PICTURES) would not appear in the gallery. Once I changed the directory to another (random folder on the sdcard) it showed up. Now that I think about it, I remember seeing this before. You should be able to do this, in fact that directory is called out in an example in the documentation here: http://developer.android.com/reference/android/content/Context.html#getExternalFilesDir(java.lang.String). Android bug? It was with 4.3. – hooby3dfx Mar 02 '14 at 02:56
  • Just a note that scanning the old path creates 0-byte files on some devices. – Learn OpenGL ES Jul 08 '15 at 23:06
7

My answer is a little late, but it might help those, who save a new file, and would like to extend the media store by just that file on Android Kitkat: On Android Kitkat the intent ACTION_MEDIA_MOUNTED is blocked for non-system apps (I think, because scanning the whole filesystem is pretty expensive). But it is still possible to use the intent ACTION_MEDIA_SCANNER_SCAN_FILE to add a file to the media store:

File f = new File(path to the file you would like to add to the media store ...);
try {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    Uri uri = Uri.fromFile(f);
    mediaScanIntent.setData(uri);
    sendBroadcast(mediaScanIntent);
} catch(Exception e) {
    ... 
}
user1882245
  • 101
  • 1
  • 4