Add MIME type to PersistentBlobProvider

Fixes #4536
Closes #4689
This commit is contained in:
Geonu Kang 2015-11-21 16:18:19 +09:00 committed by Moxie Marlinspike
parent bf806bd717
commit a2f478570a
6 changed files with 68 additions and 35 deletions

View File

@ -1402,7 +1402,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onImageCapture(@NonNull final byte[] imageBytes) { public void onImageCapture(@NonNull final byte[] imageBytes) {
setMedia(PersistentBlobProvider.getInstance(this).create(masterSecret, imageBytes), MediaType.IMAGE); setMedia(PersistentBlobProvider.getInstance(this)
.create(masterSecret, imageBytes, ContentType.IMAGE_JPEG),
MediaType.IMAGE);
quickAttachmentDrawer.hide(false); quickAttachmentDrawer.hide(false);
} }

View File

@ -23,6 +23,7 @@ import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -59,6 +60,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
private ViewGroup fragmentContainer; private ViewGroup fragmentContainer;
private View progressWheel; private View progressWheel;
private Uri resolvedExtra; private Uri resolvedExtra;
private String mimeType;
private boolean isPassingAlongMedia; private boolean isPassingAlongMedia;
@Override @Override
@ -110,6 +112,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
isPassingAlongMedia = false; isPassingAlongMedia = false;
Uri streamExtra = getIntent().getParcelableExtra(Intent.EXTRA_STREAM); Uri streamExtra = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
mimeType = getMimeType(streamExtra);
if (streamExtra != null && PartAuthority.isLocalUri(streamExtra)) { if (streamExtra != null && PartAuthority.isLocalUri(streamExtra)) {
isPassingAlongMedia = true; isPassingAlongMedia = true;
resolvedExtra = streamExtra; resolvedExtra = streamExtra;
@ -166,19 +169,18 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
private Intent getBaseShareIntent(final @NonNull Class<?> target) { private Intent getBaseShareIntent(final @NonNull Class<?> target) {
final Intent intent = new Intent(this, target); final Intent intent = new Intent(this, target);
final String textExtra = getIntent().getStringExtra(Intent.EXTRA_TEXT); final String textExtra = getIntent().getStringExtra(Intent.EXTRA_TEXT);
final Uri streamExtra = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
final String type = streamExtra != null ? getMimeType(streamExtra)
: MediaUtil.getCorrectedMimeType(getIntent().getType());
intent.putExtra(ConversationActivity.TEXT_EXTRA, textExtra); intent.putExtra(ConversationActivity.TEXT_EXTRA, textExtra);
if (resolvedExtra != null) intent.setDataAndType(resolvedExtra, type); if (resolvedExtra != null) intent.setDataAndType(resolvedExtra, mimeType);
return intent; return intent;
} }
private String getMimeType(Uri uri) { private String getMimeType(@Nullable Uri uri) {
final String type = MediaUtil.getMimeType(getApplicationContext(), uri); if (uri != null) {
return type == null ? MediaUtil.getCorrectedMimeType(getIntent().getType()) final String mimeType = MediaUtil.getMimeType(getApplicationContext(), uri);
: type; if (mimeType != null) return mimeType;
}
return MediaUtil.getCorrectedMimeType(getIntent().getType());
} }
private class ResolveMediaTask extends AsyncTask<Uri, Void, Uri> { private class ResolveMediaTask extends AsyncTask<Uri, Void, Uri> {
@ -200,7 +202,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
return null; return null;
} }
return PersistentBlobProvider.getInstance(context).create(masterSecret, input); return PersistentBlobProvider.getInstance(context).create(masterSecret, input, mimeType);
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w(TAG, ioe); Log.w(TAG, ioe);
return null; return null;

View File

@ -20,6 +20,8 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import ws.com.google.android.mms.ContentType;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class AudioRecorder { public class AudioRecorder {
@ -54,7 +56,9 @@ public class AudioRecorder {
ParcelFileDescriptor fds[] = ParcelFileDescriptor.createPipe(); ParcelFileDescriptor fds[] = ParcelFileDescriptor.createPipe();
captureUri = blobProvider.create(masterSecret, new ParcelFileDescriptor.AutoCloseInputStream(fds[0])); captureUri = blobProvider.create(masterSecret,
new ParcelFileDescriptor.AutoCloseInputStream(fds[0]),
ContentType.AUDIO_AAC);
audioCodec = new AudioCodec(); audioCodec = new AudioCodec();
audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1])); audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]));

View File

@ -159,7 +159,8 @@ public class AttachmentManager {
@Override @Override
public void onSuccess(@NonNull Bitmap result) { public void onSuccess(@NonNull Bitmap result) {
byte[] blob = BitmapUtil.toByteArray(result); byte[] blob = BitmapUtil.toByteArray(result);
Uri uri = PersistentBlobProvider.getInstance(context).create(masterSecret, blob); Uri uri = PersistentBlobProvider.getInstance(context)
.create(masterSecret, blob, ContentType.IMAGE_PNG);
LocationSlide locationSlide = new LocationSlide(context, uri, blob.length, place); LocationSlide locationSlide = new LocationSlide(context, uri, blob.length, place);
setSlide(locationSlide); setSlide(locationSlide);
@ -273,7 +274,8 @@ public class AttachmentManager {
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (captureIntent.resolveActivity(activity.getPackageManager()) != null) { if (captureIntent.resolveActivity(activity.getPackageManager()) != null) {
if (captureUri == null) { if (captureUri == null) {
captureUri = PersistentBlobProvider.getInstance(context).createForExternal(); captureUri = PersistentBlobProvider.getInstance(context)
.createForExternal(ContentType.IMAGE_JPEG);
} }
Log.w(TAG, "captureUri path is " + captureUri.getPath()); Log.w(TAG, "captureUri path is " + captureUri.getPath());
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, captureUri); captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, captureUri);

View File

@ -1,16 +1,18 @@
package org.thoughtcrime.securesms.providers; package org.thoughtcrime.securesms.providers;
import android.annotation.SuppressLint;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.Context; import android.content.Context;
import android.content.UriMatcher; import android.content.UriMatcher;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.webkit.MimeTypeMap;
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream; import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream; import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -18,7 +20,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -29,12 +30,14 @@ public class PersistentBlobProvider {
private static final String TAG = PersistentBlobProvider.class.getSimpleName(); private static final String TAG = PersistentBlobProvider.class.getSimpleName();
private static final String URI_STRING = "content://org.thoughtcrime.securesms/capture"; private static final String URI_STRING = "content://org.thoughtcrime.securesms/capture";
public static final Uri CONTENT_URI = Uri.parse(URI_STRING); public static final Uri CONTENT_URI = Uri.parse(URI_STRING);
public static final String AUTHORITY = "org.thoughtcrime.securesms"; public static final String AUTHORITY = "org.thoughtcrime.securesms";
public static final String EXPECTED_PATH = "capture/*/#"; public static final String EXPECTED_PATH = "capture/*/*/#";
private static final int MATCH = 1; private static final int MIMETYPE_PATH_SEGMENT = 1;
private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH) {{ private static final String BLOB_EXTENSION = "blob";
private static final int MATCH = 1;
private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH) {{
addURI(AUTHORITY, EXPECTED_PATH, MATCH); addURI(AUTHORITY, EXPECTED_PATH, MATCH);
}}; }};
@ -51,7 +54,8 @@ public class PersistentBlobProvider {
return instance; return instance;
} }
private final Context context; private final Context context;
@SuppressLint("UseSparseArrays")
private final Map<Long, byte[]> cache = Collections.synchronizedMap(new HashMap<Long, byte[]>()); private final Map<Long, byte[]> cache = Collections.synchronizedMap(new HashMap<Long, byte[]>());
private final ExecutorService executor = Executors.newCachedThreadPool(); private final ExecutorService executor = Executors.newCachedThreadPool();
@ -60,22 +64,27 @@ public class PersistentBlobProvider {
} }
public Uri create(@NonNull MasterSecret masterSecret, public Uri create(@NonNull MasterSecret masterSecret,
@NonNull byte[] imageBytes) @NonNull byte[] blobBytes,
@NonNull String mimeType)
{ {
final long id = System.currentTimeMillis(); final long id = System.currentTimeMillis();
cache.put(id, imageBytes); cache.put(id, blobBytes);
return create(masterSecret, new ByteArrayInputStream(imageBytes), id); return create(masterSecret, new ByteArrayInputStream(blobBytes), id, mimeType);
} }
public Uri create(@NonNull MasterSecret masterSecret, public Uri create(@NonNull MasterSecret masterSecret,
@NonNull InputStream input) @NonNull InputStream input,
@NonNull String mimeType)
{ {
return create(masterSecret, input, System.currentTimeMillis()); return create(masterSecret, input, System.currentTimeMillis(), mimeType);
} }
private Uri create(MasterSecret masterSecret, InputStream input, long id) { private Uri create(MasterSecret masterSecret, InputStream input, long id, String mimeType) {
persistToDisk(masterSecret, id, input); persistToDisk(masterSecret, id, input);
final Uri uniqueUri = Uri.withAppendedPath(CONTENT_URI, String.valueOf(System.currentTimeMillis())); final Uri uniqueUri = CONTENT_URI.buildUpon()
.appendPath(mimeType)
.appendEncodedPath(String.valueOf(System.currentTimeMillis()))
.build();
return ContentUris.withAppendedId(uniqueUri, id); return ContentUris.withAppendedId(uniqueUri, id);
} }
@ -97,10 +106,9 @@ public class PersistentBlobProvider {
}); });
} }
public Uri createForExternal() throws IOException { public Uri createForExternal(@NonNull String mimeType) throws IOException {
return Uri.fromFile(new File(getExternalDir(context), String.valueOf(System.currentTimeMillis()) + ".jpg")) return Uri.fromFile(new File(getExternalDir(context),
.buildUpon() String.valueOf(System.currentTimeMillis()) + "." + getExtensionFromMimeType(mimeType)));
.build();
} }
public boolean delete(@NonNull Uri uri) { public boolean delete(@NonNull Uri uri) {
@ -121,7 +129,17 @@ public class PersistentBlobProvider {
} }
private File getFile(long id) { private File getFile(long id) {
return new File(context.getDir("captures", Context.MODE_PRIVATE), id + ".jpg"); return new File(context.getDir("captures", Context.MODE_PRIVATE), id + "." + BLOB_EXTENSION);
}
public static @Nullable String getMimeType(@NonNull Context context, @NonNull Uri persistentBlobUri) {
if (!isAuthority(context, persistentBlobUri)) return null;
return persistentBlobUri.getPathSegments().get(MIMETYPE_PATH_SEGMENT);
}
private static @NonNull String getExtensionFromMimeType(String mimeType) {
final String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
return extension != null ? extension : BLOB_EXTENSION;
} }
private static @NonNull File getExternalDir(Context context) throws IOException { private static @NonNull File getExternalDir(Context context) throws IOException {

View File

@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -71,10 +72,14 @@ public class MediaUtil {
} }
public static @Nullable String getMimeType(Context context, Uri uri) { public static @Nullable String getMimeType(Context context, Uri uri) {
if (PersistentBlobProvider.isAuthority(context, uri)) {
return PersistentBlobProvider.getMimeType(context, uri);
}
String type = context.getContentResolver().getType(uri); String type = context.getContentResolver().getType(uri);
if (type == null) { if (type == null) {
final String extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString()); final String extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
} }
return getCorrectedMimeType(type); return getCorrectedMimeType(type);
} }