From 0021e229d8844c426763dc3d0681359c9845a4a3 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Mon, 7 Aug 2023 13:27:55 -0300 Subject: [PATCH] Add virtual file support to file sharing. --- .../securesms/mms/PartAuthority.java | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java index 68e7c657b3..a1461b58e5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java @@ -3,10 +3,14 @@ package org.thoughtcrime.securesms.mms; import android.content.ContentUris; import android.content.Context; import android.content.UriMatcher; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; import android.net.Uri; +import android.provider.DocumentsContract; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.provider.DocumentsContractCompat; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.attachments.Attachment; @@ -20,6 +24,7 @@ import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider; import org.thoughtcrime.securesms.providers.PartProvider; import org.thoughtcrime.securesms.wallpaper.WallpaperStorage; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -78,7 +83,7 @@ public class PartAuthority { case WALLPAPER_ROW: return WallpaperStorage.read(context, getWallpaperFilename(uri)); case EMOJI_ROW: return EmojiFiles.openForReading(context, getEmojiFilename(uri)); case AVATAR_PICKER_ROW: return AvatarPickerStorage.read(context, getAvatarPickerFilename(uri)); - default: return context.getContentResolver().openInputStream(uri); + default: return openExternalFileStream(context, uri); } } catch (SecurityException se) { throw new IOException(se); @@ -223,4 +228,45 @@ public class PartAuthority { public static @NonNull AttachmentId requireAttachmentId(@NonNull Uri uri) { return new PartUriParser(uri).getPartId(); } + + private static @Nullable InputStream openExternalFileStream(@NonNull Context context, @NonNull Uri uri) throws IOException { + if (isVirtualFile(context, uri)) { + return getInputStreamForVirtualFile(context, uri); + } else { + return context.getContentResolver().openInputStream(uri); + } + } + + private static boolean isVirtualFile(@NonNull Context context, @NonNull Uri uri) { + if (!DocumentsContractCompat.isDocumentUri(context, uri)) { + return false; + } + + try (Cursor cursor = context.getContentResolver().query(uri, new String[]{DocumentsContract.Document.COLUMN_FLAGS}, null, null, null, null)) { + if (cursor == null) { + return false; + } + + int flags = cursor.moveToFirst() ? cursor.getInt(0) : 0; + return (flags & DocumentsContractCompat.DocumentCompat.FLAG_VIRTUAL_DOCUMENT) != 0; + } + } + + /** @noinspection resource*/ + private static @Nullable InputStream getInputStreamForVirtualFile(@NonNull Context context, @NonNull Uri uri) throws IOException { + String[] openableMimeTypes = context.getContentResolver().getStreamTypes(uri, "*/*"); + + if (openableMimeTypes == null || openableMimeTypes.length < 1) { + throw new FileNotFoundException("No openable mime-types for virtual file."); + } + + AssetFileDescriptor fileDescriptor = context.getContentResolver() + .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null); + + if (fileDescriptor == null) { + throw new FileNotFoundException("Couldn't open file descriptor for virtual file."); + } + + return fileDescriptor.createInputStream(); + } }