4

Using the SAF provided in KitKat, the MediaScanner is not called on files saved to the internal or external storage points of a device. As such I have to determine based on the returned URI if I should attempt to run MediaScanner.

// The SAF uses content URI to pass meta about the file. The following host is used for internal storage.
if (mExportServiceUri.getHost().equals("com.android.externalstorage.documents")) {
    final File externalStorage = Environment.getExternalStorageDirectory();
    final String path = mExportServiceUri.getEncodedPath().replace("/document/primary%3A", "");
    MediaScannerConnection.scanFile(mService.getApplicationContext(), new String[] { new File(
            externalStorage, path).getAbsolutePath() }, null, null);
}

Has anyone else had to work around this issue and if so, is there a better approach than this? Currently this only supports the device external storage and additional storage space such as the SDCard need to be handled in a separate check.

ian.shaun.thomas
  • 3,468
  • 25
  • 40
  • I believe this resolves my issues though I am concerned that it may not work on all devices going forward or even currently. I am still open to suggestions for better MediaScanner handling. – ian.shaun.thomas Feb 06 '14 at 15:52

1 Answers1

0

To support what I believe to be all possible mounts including USB thumb drives connected via OTG or even possibly directly to the full size usb port on some tablets (I dont have a tablet to test that with, does a 4.4 tablet even exist with a full size port?) I have the following which seems to work well on a Galaxy S4 (Play store edition) and a N5.

// The SAF uses content URI to pass meta about the file. The following host is used for SD storage.
if (mExportServiceUri.getHost().equals("com.android.externalstorage.documents")) {
    final String encodedPath = mExportServiceUri.getEncodedPath();
    final String path = encodedPath.substring(encodedPath.indexOf("%3A") + 3);
    final File[] storagePoints = new File("/storage").listFiles();

    // document/primary is in /storage/emulated/legacy and thus will fail the exists check in the else handling loop check
    if (encodedPath.startsWith("/document/primary")) {
        // External file stored in Environment path
        final File externalFile = new File(Environment.getExternalStorageDirectory(), path);
        MediaScannerConnection.scanFile(mService.getApplicationContext(),
                new String[] { externalFile.getAbsolutePath() }, null, null);
    } else {
        // External file stored in one of the mount points, check each mount point for the file
        for (int i = 0, j = storagePoints.length; i < j; ++i) {
            final File externalFile = new File(storagePoints[i], path);
            if (externalFile.exists()) {
                MediaScannerConnection.scanFile(mService.getApplicationContext(),
                        new String[] { externalFile.getAbsolutePath() }, null, null);
                break;
            }
        }
    }
}
ian.shaun.thomas
  • 3,468
  • 25
  • 40
  • I'm trying to use SAF to add mass USB storage support for Nexus 5 and other phones that can't otherwise mount USB. But, I'm having trouble figuring out how SAF fits in and how this code snippet fits into SAF. It seems like we should be extending DocumentsProvider and then adding the mount point to queryRoots(). But, if we can find the root with the above method, what value does SAF add if my goal is to just open a file within my app and not make it available to other apps? – Mike Ortiz Aug 13 '15 at 19:37
  • SAF is for working with world readable files on the phone without needing to know their location as well as working with files that you may otherwise not be able to access like inside other applications or in cloud storage. If you wish to work with files internal to your app that you do not wish other apps to use, the SAF is not what you are looking for. An internal browser would be advisable as you can likely display your internal files in a way that makes more sense to your application than a generic file browser can do. – ian.shaun.thomas Aug 13 '15 at 21:34