diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 0e470fc78a..fbf651d436 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -1402,7 +1402,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override 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); } diff --git a/src/org/thoughtcrime/securesms/ShareActivity.java b/src/org/thoughtcrime/securesms/ShareActivity.java index 367c15c489..0195942085 100644 --- a/src/org/thoughtcrime/securesms/ShareActivity.java +++ b/src/org/thoughtcrime/securesms/ShareActivity.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -59,6 +60,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity private ViewGroup fragmentContainer; private View progressWheel; private Uri resolvedExtra; + private String mimeType; private boolean isPassingAlongMedia; @Override @@ -110,6 +112,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity isPassingAlongMedia = false; Uri streamExtra = getIntent().getParcelableExtra(Intent.EXTRA_STREAM); + mimeType = getMimeType(streamExtra); if (streamExtra != null && PartAuthority.isLocalUri(streamExtra)) { isPassingAlongMedia = true; resolvedExtra = streamExtra; @@ -166,19 +169,18 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity private Intent getBaseShareIntent(final @NonNull Class target) { final Intent intent = new Intent(this, target); 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); - if (resolvedExtra != null) intent.setDataAndType(resolvedExtra, type); + if (resolvedExtra != null) intent.setDataAndType(resolvedExtra, mimeType); return intent; } - private String getMimeType(Uri uri) { - final String type = MediaUtil.getMimeType(getApplicationContext(), uri); - return type == null ? MediaUtil.getCorrectedMimeType(getIntent().getType()) - : type; + private String getMimeType(@Nullable Uri uri) { + if (uri != null) { + final String mimeType = MediaUtil.getMimeType(getApplicationContext(), uri); + if (mimeType != null) return mimeType; + } + return MediaUtil.getCorrectedMimeType(getIntent().getType()); } private class ResolveMediaTask extends AsyncTask { @@ -200,7 +202,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity return null; } - return PersistentBlobProvider.getInstance(context).create(masterSecret, input); + return PersistentBlobProvider.getInstance(context).create(masterSecret, input, mimeType); } catch (IOException ioe) { Log.w(TAG, ioe); return null; diff --git a/src/org/thoughtcrime/securesms/audio/AudioRecorder.java b/src/org/thoughtcrime/securesms/audio/AudioRecorder.java index 9c1060b546..d110d8a614 100644 --- a/src/org/thoughtcrime/securesms/audio/AudioRecorder.java +++ b/src/org/thoughtcrime/securesms/audio/AudioRecorder.java @@ -20,6 +20,8 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture; import java.io.IOException; import java.util.concurrent.ExecutorService; +import ws.com.google.android.mms.ContentType; + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class AudioRecorder { @@ -54,7 +56,9 @@ public class AudioRecorder { 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.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1])); diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java index 86f3df9e10..ded9179015 100644 --- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -159,7 +159,8 @@ public class AttachmentManager { @Override public void onSuccess(@NonNull Bitmap 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); setSlide(locationSlide); @@ -273,7 +274,8 @@ public class AttachmentManager { Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (captureIntent.resolveActivity(activity.getPackageManager()) != 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()); captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, captureUri); diff --git a/src/org/thoughtcrime/securesms/providers/PersistentBlobProvider.java b/src/org/thoughtcrime/securesms/providers/PersistentBlobProvider.java index 2ee7a3a20c..2ee0d206ed 100644 --- a/src/org/thoughtcrime/securesms/providers/PersistentBlobProvider.java +++ b/src/org/thoughtcrime/securesms/providers/PersistentBlobProvider.java @@ -1,16 +1,18 @@ package org.thoughtcrime.securesms.providers; +import android.annotation.SuppressLint; import android.content.ContentUris; import android.content.Context; import android.content.UriMatcher; import android.net.Uri; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; +import android.webkit.MimeTypeMap; import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream; import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream; import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.util.Util; import java.io.ByteArrayInputStream; @@ -18,7 +20,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -29,12 +30,14 @@ public class PersistentBlobProvider { private static final String TAG = PersistentBlobProvider.class.getSimpleName(); - private static final String URI_STRING = "content://org.thoughtcrime.securesms/capture"; - public static final Uri CONTENT_URI = Uri.parse(URI_STRING); - public static final String AUTHORITY = "org.thoughtcrime.securesms"; - public static final String EXPECTED_PATH = "capture/*/#"; - private static final int MATCH = 1; - private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH) {{ + private static final String URI_STRING = "content://org.thoughtcrime.securesms/capture"; + public static final Uri CONTENT_URI = Uri.parse(URI_STRING); + public static final String AUTHORITY = "org.thoughtcrime.securesms"; + public static final String EXPECTED_PATH = "capture/*/*/#"; + private static final int MIMETYPE_PATH_SEGMENT = 1; + 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); }}; @@ -51,7 +54,8 @@ public class PersistentBlobProvider { return instance; } - private final Context context; + private final Context context; + @SuppressLint("UseSparseArrays") private final Map cache = Collections.synchronizedMap(new HashMap()); private final ExecutorService executor = Executors.newCachedThreadPool(); @@ -60,22 +64,27 @@ public class PersistentBlobProvider { } public Uri create(@NonNull MasterSecret masterSecret, - @NonNull byte[] imageBytes) + @NonNull byte[] blobBytes, + @NonNull String mimeType) { final long id = System.currentTimeMillis(); - cache.put(id, imageBytes); - return create(masterSecret, new ByteArrayInputStream(imageBytes), id); + cache.put(id, blobBytes); + return create(masterSecret, new ByteArrayInputStream(blobBytes), id, mimeType); } 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); - 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); } @@ -97,10 +106,9 @@ public class PersistentBlobProvider { }); } - public Uri createForExternal() throws IOException { - return Uri.fromFile(new File(getExternalDir(context), String.valueOf(System.currentTimeMillis()) + ".jpg")) - .buildUpon() - .build(); + public Uri createForExternal(@NonNull String mimeType) throws IOException { + return Uri.fromFile(new File(getExternalDir(context), + String.valueOf(System.currentTimeMillis()) + "." + getExtensionFromMimeType(mimeType))); } public boolean delete(@NonNull Uri uri) { @@ -121,7 +129,17 @@ public class PersistentBlobProvider { } 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 { diff --git a/src/org/thoughtcrime/securesms/util/MediaUtil.java b/src/org/thoughtcrime/securesms/util/MediaUtil.java index 45f1db9b69..883a2787fc 100644 --- a/src/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/src/org/thoughtcrime/securesms/util/MediaUtil.java @@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.VideoSlide; +import org.thoughtcrime.securesms.providers.PersistentBlobProvider; import java.io.IOException; import java.io.InputStream; @@ -71,10 +72,14 @@ public class MediaUtil { } 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); if (type == null) { final String extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString()); - type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()); } return getCorrectedMimeType(type); }