From 584bd25aeb3bea74081d73f506dc80063c5b680e Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 4 Nov 2023 18:24:10 -0400 Subject: [PATCH] FileShuttleService: trigger media scanner after writes as well Previously, we have been triggering the media scanner after calls to `createFile()`. This is not robust, though, as in many cases the writer is not able to finish writing (i.e. *actually* creating the file) before the scanner is scheduled, resulting in media copied into the work profile not showing up in galleries etc. Turns out, ParcelFileDescriptor supports a `listener` argument in its `open()` calls. This listener is triggered when the fd is actually closed by the (potentially remote) writer. To have robust media scanning, we simply re-trigger the scanner every time a writable ParcelFileDescriptor is closed. --- .../shelter/services/FileShuttleService.java | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/net/typeblog/shelter/services/FileShuttleService.java b/app/src/main/java/net/typeblog/shelter/services/FileShuttleService.java index 6120328..ff6355c 100644 --- a/app/src/main/java/net/typeblog/shelter/services/FileShuttleService.java +++ b/app/src/main/java/net/typeblog/shelter/services/FileShuttleService.java @@ -101,10 +101,24 @@ public class FileShuttleService extends Service { public ParcelFileDescriptor openFile(String path, String mode) { resetSuicideTask(); File f = new File(resolvePath(path)); + int numericMode = ParcelFileDescriptor.parseMode(mode); try { - return ParcelFileDescriptor.open(f, ParcelFileDescriptor.parseMode(mode)); - } catch (FileNotFoundException e) { + if ((numericMode & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0) { + // When the file is opened in writable mode, and the file is of a media + // type, we need to notify the media scanner of the update. + // Even though this is done as part of file creation as well, that scan + // might have failed because it happened before the writer was able to + // finish writing. + return ParcelFileDescriptor.open(f, numericMode, mHandler, (e) -> { + String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension( + Utility.getFileExtension(f.getAbsolutePath())); + notifyMediaScannerIfNecessary(f, mime); + }); + } else { + return ParcelFileDescriptor.open(f, numericMode); + } + } catch (IOException e) { return null; } } @@ -138,8 +152,6 @@ public class FileShuttleService extends Service { DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType); boolean shouldAppendExtension = mimeType != null && !isDirectory && !mimeType.equals("application/octet-stream"); - boolean isMedia = - mimeType != null && (mimeType.startsWith("image/") || mimeType.startsWith("video/")); // Append extension for files if a MIME type is specified if (shouldAppendExtension) { @@ -159,13 +171,7 @@ public class FileShuttleService extends Service { return null; } - // Notify the media scanner to scan the file as needed - // This has to be done AFTER file creation - if (isMedia) { - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); - intent.setData(Uri.fromFile(f)); - sendBroadcast(intent); - } + notifyMediaScannerIfNecessary(f, mimeType); return f.getAbsolutePath(); } @@ -299,4 +305,14 @@ public class FileShuttleService extends Service { return pair[0]; } + + private void notifyMediaScannerIfNecessary(File f, String mimeType) { + // Notify the media scanner to scan the file as needed + // This has to be done AFTER file creation + if (mimeType != null && (mimeType.startsWith("image/") || mimeType.startsWith("video/"))) { + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + intent.setData(Uri.fromFile(f)); + sendBroadcast(intent); + } + } }