mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 16:49:40 +01:00
Share media from within Media Preview and share QR code image.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package org.thoughtcrime.securesms.providers;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.provider.OpenableColumns;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
abstract class BaseContentProvider extends ContentProvider {
|
||||
|
||||
private static final String[] COLUMNS = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
|
||||
|
||||
/**
|
||||
* Sanity checks the security like FileProvider does.
|
||||
*/
|
||||
@Override
|
||||
public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) {
|
||||
super.attachInfo(context, info);
|
||||
|
||||
if (info.exported) {
|
||||
throw new SecurityException("Provider must not be exported");
|
||||
}
|
||||
if (!info.grantUriPermissions) {
|
||||
throw new SecurityException("Provider must grant uri permissions");
|
||||
}
|
||||
}
|
||||
|
||||
protected static Cursor createCursor(@Nullable String[] projection, @NonNull String fileName, long fileSize) {
|
||||
if (projection == null || projection.length == 0) {
|
||||
projection = COLUMNS;
|
||||
}
|
||||
|
||||
ArrayList<String> cols = new ArrayList<>(projection.length);
|
||||
ArrayList<Object> values = new ArrayList<>(projection.length);
|
||||
|
||||
for (String col : projection) {
|
||||
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
|
||||
cols.add(OpenableColumns.DISPLAY_NAME);
|
||||
values.add(fileName);
|
||||
} else if (OpenableColumns.SIZE.equals(col)) {
|
||||
cols.add(OpenableColumns.SIZE);
|
||||
values.add(fileSize);
|
||||
}
|
||||
}
|
||||
|
||||
MatrixCursor cursor = new MatrixCursor(cols.toArray(new String[0]), 1);
|
||||
|
||||
cursor.addRow(values.toArray(new Object[0]));
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
protected static String createFileNameForMimeType(String mimeType) {
|
||||
return mimeType.replace('/', '.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.thoughtcrime.securesms.providers;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.MemoryFile;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.MemoryFileUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public final class BlobContentProvider extends BaseContentProvider {
|
||||
|
||||
private static final String TAG = Log.tag(BlobContentProvider.class);
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
Log.i(TAG, "onCreate()");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
||||
Log.i(TAG, "openFile() called: " + uri);
|
||||
|
||||
try {
|
||||
try (InputStream stream = BlobProvider.getInstance().getStream(ApplicationDependencies.getApplication(), uri)) {
|
||||
Long fileSize = BlobProvider.getFileSize(uri);
|
||||
if (fileSize == null) {
|
||||
Log.w(TAG, "No file size available");
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
return getParcelStreamForStream(stream, Util.toIntExact(fileSize));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
private static @NonNull ParcelFileDescriptor getParcelStreamForStream(@NonNull InputStream in, int fileSize) throws IOException {
|
||||
MemoryFile memoryFile = new MemoryFile(null, fileSize);
|
||||
|
||||
try (OutputStream out = memoryFile.getOutputStream()) {
|
||||
Util.copy(in, out);
|
||||
}
|
||||
|
||||
return MemoryFileUtil.getParcelFileDescriptor(memoryFile);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
|
||||
Log.i(TAG, "query() called: " + uri);
|
||||
|
||||
if (projection == null || projection.length <= 0) return null;
|
||||
|
||||
String mimeType = BlobProvider.getMimeType(uri);
|
||||
String fileName = BlobProvider.getFileName(uri);
|
||||
Long fileSize = BlobProvider.getFileSize(uri);
|
||||
|
||||
if (fileSize == null) {
|
||||
Log.w(TAG, "No file size");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mimeType == null) {
|
||||
Log.w(TAG, "No mime type");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (fileName == null) {
|
||||
fileName = createFileNameForMimeType(mimeType);
|
||||
}
|
||||
|
||||
return createCursor(projection, fileName, fileSize);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
return BlobProvider.getMimeType(uri);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class BlobProvider {
|
||||
private static final String MULTI_SESSION_DIRECTORY = "multi_session_blobs";
|
||||
private static final String SINGLE_SESSION_DIRECTORY = "single_session_blobs";
|
||||
|
||||
public static final String AUTHORITY = BuildConfig.APPLICATION_ID;
|
||||
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".blob";
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/blob");
|
||||
public static final String PATH = "blob/*/*/*/*/*";
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.providers;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
@@ -35,7 +34,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class MmsBodyProvider extends ContentProvider {
|
||||
public final class MmsBodyProvider extends BaseContentProvider {
|
||||
private static final String TAG = MmsBodyProvider.class.getSimpleName();
|
||||
private static final String CONTENT_AUTHORITY = BuildConfig.APPLICATION_ID + ".mms";
|
||||
private static final String CONTENT_URI_STRING = "content://" + CONTENT_AUTHORITY + "/mms";
|
||||
|
||||
@@ -16,18 +16,16 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.providers;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.MemoryFile;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.OpenableColumns;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
@@ -44,9 +42,9 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class PartProvider extends ContentProvider {
|
||||
public final class PartProvider extends BaseContentProvider {
|
||||
|
||||
private static final String TAG = PartProvider.class.getSimpleName();
|
||||
private static final String TAG = Log.tag(PartProvider.class);
|
||||
|
||||
private static final String CONTENT_AUTHORITY = BuildConfig.APPLICATION_ID + ".part";
|
||||
private static final String CONTENT_URI_STRING = "content://" + CONTENT_AUTHORITY + "/part";
|
||||
@@ -80,8 +78,7 @@ public class PartProvider extends ContentProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case SINGLE_ROW:
|
||||
if (uriMatcher.match(uri) == SINGLE_ROW) {
|
||||
Log.i(TAG, "Parting out a single row...");
|
||||
try {
|
||||
final PartUriParser partUri = new PartUriParser(uri);
|
||||
@@ -105,15 +102,14 @@ public class PartProvider extends ContentProvider {
|
||||
public String getType(@NonNull Uri uri) {
|
||||
Log.i(TAG, "getType() called: " + uri);
|
||||
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case SINGLE_ROW:
|
||||
PartUriParser partUriParser = new PartUriParser(uri);
|
||||
DatabaseAttachment attachment = DatabaseFactory.getAttachmentDatabase(getContext())
|
||||
.getAttachment(partUriParser.getPartId());
|
||||
if (uriMatcher.match(uri) == SINGLE_ROW) {
|
||||
PartUriParser partUriParser = new PartUriParser(uri);
|
||||
DatabaseAttachment attachment = DatabaseFactory.getAttachmentDatabase(getContext()).getAttachment(partUriParser.getPartId());
|
||||
|
||||
if (attachment != null) {
|
||||
return attachment.getContentType();
|
||||
}
|
||||
if (attachment != null) {
|
||||
Log.i(TAG, "getType() called: " + uri + " It's " + attachment.getContentType());
|
||||
return attachment.getContentType();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -126,32 +122,29 @@ public class PartProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
public Cursor query(@NonNull Uri url, @Nullable String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
Log.i(TAG, "query() called: " + url);
|
||||
|
||||
if (projection == null || projection.length <= 0) return null;
|
||||
if (uriMatcher.match(url) == SINGLE_ROW) {
|
||||
PartUriParser partUri = new PartUriParser(url);
|
||||
DatabaseAttachment attachment = DatabaseFactory.getAttachmentDatabase(getContext()).getAttachment(partUri.getPartId());
|
||||
|
||||
switch (uriMatcher.match(url)) {
|
||||
case SINGLE_ROW:
|
||||
PartUriParser partUri = new PartUriParser(url);
|
||||
DatabaseAttachment attachment = DatabaseFactory.getAttachmentDatabase(getContext()).getAttachment(partUri.getPartId());
|
||||
if (attachment == null) return null;
|
||||
|
||||
if (attachment == null) return null;
|
||||
long fileSize = attachment.getSize();
|
||||
|
||||
MatrixCursor matrixCursor = new MatrixCursor(projection, 1);
|
||||
Object[] resultRow = new Object[projection.length];
|
||||
if (fileSize <= 0) {
|
||||
Log.w(TAG, "Empty file " + fileSize);
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i=0;i<projection.length;i++) {
|
||||
if (OpenableColumns.DISPLAY_NAME.equals(projection[i])) {
|
||||
resultRow[i] = attachment.getFileName();
|
||||
}
|
||||
}
|
||||
String fileName = attachment.getFileName() != null ? attachment.getFileName()
|
||||
: createFileNameForMimeType(attachment.getContentType());
|
||||
|
||||
matrixCursor.addRow(resultRow);
|
||||
return matrixCursor;
|
||||
return createCursor(projection, fileName, fileSize);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user