Compare commits

..

4 commits

Author SHA1 Message Date
Peter Cai 3593861e39 CHANGELOG: describe media scanning changes 2023-11-04 18:27:58 -04:00
Peter Cai 584bd25aeb 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.
2023-11-04 18:24:10 -04:00
Peter Cai 20369aafd2 gradle: Set versionCode of debug builds to current timestamp 2023-11-04 18:14:12 -04:00
Peter Cai edb8447d2d FileShuttle: use try-with-resource for FileOutputStream 2023-11-04 16:06:02 -04:00
3 changed files with 36 additions and 15 deletions

View file

@ -7,6 +7,7 @@
- Removed "Fake Camera" feature as it has not been supported since R. - Removed "Fake Camera" feature as it has not been supported since R.
- Version displayed within the app has now been changed to also reflect the exact Git commit when the app is built. - Version displayed within the app has now been changed to also reflect the exact Git commit when the app is built.
- File Shuttle no longer appends ".null" or ".bin" suffixes unnecessarily. This should make it work much better with file managers such as Material Files. - File Shuttle no longer appends ".null" or ".bin" suffixes unnecessarily. This should make it work much better with file managers such as Material Files.
- File Shuttle now triggers media scanning much more robustly. Media files (pictures, videos, etc.) copied into the work profile should now show up much quicker in gallery apps.
1.8 1.8
=== ===

View file

@ -55,6 +55,11 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
applicationVariants.all { variant ->
if (variant.name == "debug") {
variant.outputs.each { o -> o.versionCodeOverride = System.currentTimeSeconds() }
}
}
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8

View file

@ -101,10 +101,24 @@ public class FileShuttleService extends Service {
public ParcelFileDescriptor openFile(String path, String mode) { public ParcelFileDescriptor openFile(String path, String mode) {
resetSuicideTask(); resetSuicideTask();
File f = new File(resolvePath(path)); File f = new File(resolvePath(path));
int numericMode = ParcelFileDescriptor.parseMode(mode);
try { try {
return ParcelFileDescriptor.open(f, ParcelFileDescriptor.parseMode(mode)); if ((numericMode & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0) {
} catch (FileNotFoundException e) { // 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; return null;
} }
} }
@ -138,8 +152,6 @@ public class FileShuttleService extends Service {
DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType); DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
boolean shouldAppendExtension = boolean shouldAppendExtension =
mimeType != null && !isDirectory && !mimeType.equals("application/octet-stream"); 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 // Append extension for files if a MIME type is specified
if (shouldAppendExtension) { if (shouldAppendExtension) {
@ -159,13 +171,7 @@ public class FileShuttleService extends Service {
return null; return null;
} }
// Notify the media scanner to scan the file as needed notifyMediaScannerIfNecessary(f, mimeType);
// 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);
}
return f.getAbsolutePath(); return f.getAbsolutePath();
} }
@ -284,14 +290,13 @@ public class FileShuttleService extends Service {
return null; return null;
} }
FileOutputStream os = new FileOutputStream(pair[1].getFileDescriptor());
// Send the bitmap into the pipe in another thread, so that we can return the // Send the bitmap into the pipe in another thread, so that we can return the
// reading fd to the Documents UI before we finish sending the Bitmap. // reading fd to the Documents UI before we finish sending the Bitmap.
new Thread(() -> { new Thread(() -> {
bmp.compress(Bitmap.CompressFormat.PNG, 100, os); try (FileOutputStream os = new FileOutputStream(pair[1].getFileDescriptor())) {
try { bmp.compress(Bitmap.CompressFormat.PNG, 100, os);
os.flush(); os.flush();
os.close();
} catch (IOException e) { } catch (IOException e) {
// ... // ...
} }
@ -300,4 +305,14 @@ public class FileShuttleService extends Service {
return pair[0]; 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);
}
}
} }