diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java index 15f56b2656..295d988aea 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java +++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java @@ -21,6 +21,7 @@ import android.util.SparseArray; import android.widget.TextView; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.ListenableFutureTask; @@ -29,7 +30,6 @@ import org.thoughtcrime.securesms.util.Util; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -221,7 +221,7 @@ public class EmojiProvider { try { Log.w(TAG, "loading page " + model.getSprite()); return loadPage(); - } catch (IOException | ExecutionException ioe) { + } catch (IOException ioe) { Log.w(TAG, ioe); } return null; @@ -242,7 +242,7 @@ public class EmojiProvider { return task; } - private Bitmap loadPage() throws IOException, ExecutionException { + private Bitmap loadPage() throws IOException { if (bitmapReference != null && bitmapReference.get() != null) return bitmapReference.get(); try { @@ -252,9 +252,9 @@ public class EmojiProvider { bitmapReference = new SoftReference<>(bitmap); Log.w(TAG, "onPageLoaded(" + model.getSprite() + ")"); return bitmap; - } catch (ExecutionException e) { + } catch (BitmapDecodingException e) { Log.w(TAG, e); - throw e; + throw new IOException(e); } } diff --git a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java index 2dbd5ea361..fc260acd1b 100644 --- a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java @@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; import org.thoughtcrime.securesms.push.TextSecurePushTrustStore; +import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.jobqueue.JobParameters; @@ -63,7 +64,7 @@ public class AvatarDownloadJob extends MasterSecretJob { database.updateAvatar(groupId, avatar); } - } catch (ExecutionException | NonSuccessfulResponseCodeException e) { + } catch (BitmapDecodingException | NonSuccessfulResponseCodeException e) { Log.w(TAG, e); } finally { if (attachment != null) diff --git a/src/org/thoughtcrime/securesms/mms/MediaConstraints.java b/src/org/thoughtcrime/securesms/mms/MediaConstraints.java index 9c00dc7ab3..69e5db275f 100644 --- a/src/org/thoughtcrime/securesms/mms/MediaConstraints.java +++ b/src/org/thoughtcrime/securesms/mms/MediaConstraints.java @@ -10,13 +10,13 @@ import android.util.Pair; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; +import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.MediaUtil; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.ExecutionException; import ws.com.google.android.mms.ContentType; @@ -50,10 +50,14 @@ public abstract class MediaConstraints { } public boolean isWithinBounds(Context context, MasterSecret masterSecret, Uri uri) throws IOException { - InputStream is = PartAuthority.getAttachmentStream(context, masterSecret, uri); - Pair dimensions = BitmapUtil.getDimensions(is); - return dimensions.first > 0 && dimensions.first <= getImageMaxWidth(context) && - dimensions.second > 0 && dimensions.second <= getImageMaxHeight(context); + try { + InputStream is = PartAuthority.getAttachmentStream(context, masterSecret, uri); + Pair dimensions = BitmapUtil.getDimensions(is); + return dimensions.first > 0 && dimensions.first <= getImageMaxWidth(context) && + dimensions.second > 0 && dimensions.second <= getImageMaxHeight(context); + } catch (BitmapDecodingException e) { + throw new IOException(e); + } } public boolean canResize(@Nullable Attachment attachment) { @@ -73,8 +77,8 @@ public abstract class MediaConstraints { // XXX - This is loading everything into memory! We want the send path to be stream-like. return new MediaStream(new ByteArrayInputStream(BitmapUtil.createScaledBytes(context, new DecryptableUri(masterSecret, attachment.getDataUri()), this)), ContentType.IMAGE_JPEG); - } catch (ExecutionException ee) { - throw new IOException(ee); + } catch (BitmapDecodingException e) { + throw new IOException(e); } } } diff --git a/src/org/thoughtcrime/securesms/util/BitmapDecodingException.java b/src/org/thoughtcrime/securesms/util/BitmapDecodingException.java index 304dbcb023..c106159434 100644 --- a/src/org/thoughtcrime/securesms/util/BitmapDecodingException.java +++ b/src/org/thoughtcrime/securesms/util/BitmapDecodingException.java @@ -1,7 +1,12 @@ package org.thoughtcrime.securesms.util; -public class BitmapDecodingException extends Throwable { +public class BitmapDecodingException extends Exception { + public BitmapDecodingException(String s) { super(s); } + + public BitmapDecodingException(Exception nested) { + super(nested); + } } diff --git a/src/org/thoughtcrime/securesms/util/BitmapUtil.java b/src/org/thoughtcrime/securesms/util/BitmapUtil.java index 62b5363b67..1da2168063 100644 --- a/src/org/thoughtcrime/securesms/util/BitmapUtil.java +++ b/src/org/thoughtcrime/securesms/util/BitmapUtil.java @@ -30,10 +30,10 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; public class BitmapUtil { + private static final String TAG = BitmapUtil.class.getSimpleName(); private static final int MAX_COMPRESSION_QUALITY = 80; @@ -41,7 +41,7 @@ public class BitmapUtil { private static final int MAX_COMPRESSION_ATTEMPTS = 4; public static byte[] createScaledBytes(Context context, T model, MediaConstraints constraints) - throws ExecutionException, IOException + throws BitmapDecodingException { int quality = MAX_COMPRESSION_QUALITY; int attempts = 0; @@ -62,7 +62,7 @@ public class BitmapUtil { } while (bytes.length > constraints.getImageMaxSize() && attempts++ < MAX_COMPRESSION_ATTEMPTS); if (bytes.length > constraints.getImageMaxSize()) { - throw new IOException("Unable to scale image below: " + bytes.length); + throw new BitmapDecodingException("Unable to scale image below: " + bytes.length); } Log.w(TAG, "createScaledBytes(" + model.toString() + ") -> quality " + Math.min(quality, MAX_COMPRESSION_QUALITY) + ", " + attempts + " attempt(s)"); return bytes; @@ -72,7 +72,7 @@ public class BitmapUtil { } public static Bitmap createScaledBitmap(Context context, T model, int maxWidth, int maxHeight) - throws ExecutionException + throws BitmapDecodingException { final Pair dimensions = getDimensions(getInputStreamForModel(context, model)); final Pair clamped = clampDimensions(dimensions.first, dimensions.second, @@ -81,19 +81,19 @@ public class BitmapUtil { } private static InputStream getInputStreamForModel(Context context, T model) - throws ExecutionException + throws BitmapDecodingException { try { return Glide.buildStreamModelLoader(model, context) .getResourceFetcher(model, -1, -1) .loadData(Priority.NORMAL); } catch (Exception e) { - throw new ExecutionException(e); + throw new BitmapDecodingException(e); } } private static Bitmap createScaledBitmapInto(Context context, T model, int width, int height) - throws ExecutionException + throws BitmapDecodingException { final Bitmap rough = Downsampler.AT_LEAST.decode(getInputStreamForModel(context, model), Glide.get(context).getBitmapPool(), @@ -104,20 +104,22 @@ public class BitmapUtil { final Resource result = new FitCenter(context).transform(resource, width, height); if (result == null) { - throw new ExecutionException(new BitmapDecodingException("unable to transform Bitmap")); + throw new BitmapDecodingException("unable to transform Bitmap"); } return result.get(); } public static Bitmap createScaledBitmap(Context context, T model, float scale) - throws ExecutionException + throws BitmapDecodingException { Pair dimens = getDimensions(getInputStreamForModel(context, model)); return createScaledBitmapInto(context, model, (int)(dimens.first * scale), (int)(dimens.second * scale)); } - private static BitmapFactory.Options getImageDimensions(InputStream inputStream) { + private static BitmapFactory.Options getImageDimensions(InputStream inputStream) + throws BitmapDecodingException + { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BufferedInputStream fis = new BufferedInputStream(inputStream); @@ -127,10 +129,15 @@ public class BitmapUtil { } catch (IOException ioe) { Log.w(TAG, "failed to close the InputStream after reading image dimensions"); } + + if (options.outWidth == -1 || options.outHeight == -1) { + throw new BitmapDecodingException("Failed to decode image dimensions: " + options.outWidth + ", " + options.outHeight); + } + return options; } - public static Pair getDimensions(InputStream inputStream) { + public static Pair getDimensions(InputStream inputStream) throws BitmapDecodingException { BitmapFactory.Options options = getImageDimensions(inputStream); return new Pair<>(options.outWidth, options.outHeight); } diff --git a/src/org/thoughtcrime/securesms/util/MediaUtil.java b/src/org/thoughtcrime/securesms/util/MediaUtil.java index 6caaa83173..45f1db9b69 100644 --- a/src/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/src/org/thoughtcrime/securesms/util/MediaUtil.java @@ -30,7 +30,7 @@ public class MediaUtil { private static final String TAG = MediaUtil.class.getSimpleName(); public static @Nullable ThumbnailData generateThumbnail(Context context, MasterSecret masterSecret, String contentType, Uri uri) - throws ExecutionException + throws BitmapDecodingException { long startMillis = System.currentTimeMillis(); ThumbnailData data = null; @@ -49,7 +49,7 @@ public class MediaUtil { } private static Bitmap generateImageThumbnail(Context context, MasterSecret masterSecret, Uri uri) - throws ExecutionException + throws BitmapDecodingException { int maxSize = context.getResources().getDimensionPixelSize(R.dimen.media_bubble_height); return BitmapUtil.createScaledBitmap(context, new DecryptableUri(masterSecret, uri), maxSize, maxSize);