Render images in a conversation true-to-size.

Previously, we were always rendering images as squares. Instead of doing
that, we now render them as close to true-to-size as possible (within
reasonable min/max width/height boundaries).
This commit is contained in:
Greyson Parrelli
2018-03-20 11:27:11 -07:00
committed by Moxie Marlinspike
parent 9f8b4cf892
commit ea374735e1
25 changed files with 376 additions and 55 deletions

View File

@@ -12,6 +12,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.*;
import android.support.annotation.WorkerThread;
import android.support.media.ExifInterface;
import android.util.Log;
import android.util.Pair;
@@ -131,6 +132,26 @@ public class BitmapUtil {
return options;
}
@Nullable
public static Pair<Integer, Integer> getExifDimensions(InputStream inputStream) throws IOException {
ExifInterface exif = new ExifInterface(inputStream);
int width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
int height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
if (width == 0 && height == 0) {
return null;
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
if (orientation == ExifInterface.ORIENTATION_ROTATE_90 ||
orientation == ExifInterface.ORIENTATION_ROTATE_270 ||
orientation == ExifInterface.ORIENTATION_TRANSVERSE ||
orientation == ExifInterface.ORIENTATION_TRANSPOSE)
{
return new Pair<>(height, width);
}
return new Pair<>(width, height);
}
public static Pair<Integer, Integer> getDimensions(InputStream inputStream) throws BitmapDecodingException {
BitmapFactory.Options options = getImageDimensions(inputStream);
return new Pair<>(options.outWidth, options.outHeight);

View File

@@ -3,18 +3,27 @@ package org.thoughtcrime.securesms.util;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.support.media.ExifInterface;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.webkit.MimeTypeMap;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.gif.GifDrawable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.DocumentSlide;
import org.thoughtcrime.securesms.mms.GifSlide;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.MmsSlide;
import org.thoughtcrime.securesms.mms.PartAuthority;
@@ -22,8 +31,10 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;
public class MediaUtil {
@@ -100,6 +111,65 @@ public class MediaUtil {
return size;
}
@WorkerThread
public static Pair<Integer, Integer> getDimensions(@NonNull Context context, @Nullable String contentType, @Nullable Uri uri) {
if (uri == null || !MediaUtil.isImageType(contentType)) {
return new Pair<>(0, 0);
}
Pair<Integer, Integer> dimens = null;
if (MediaUtil.isGif(contentType)) {
try {
GifDrawable drawable = GlideApp.with(context)
.asGif()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.load(new DecryptableUri(uri))
.submit()
.get();
dimens = new Pair<>(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
} catch (InterruptedException e) {
Log.w(TAG, "Was unable to complete work for GIF dimensions.", e);
} catch (ExecutionException e) {
Log.w(TAG, "Glide experienced an exception while trying to get GIF dimensions.", e);
}
} else {
InputStream attachmentStream = null;
try {
if (MediaUtil.isJpegType(contentType)) {
attachmentStream = PartAuthority.getAttachmentStream(context, uri);
dimens = BitmapUtil.getExifDimensions(attachmentStream);
attachmentStream.close();
attachmentStream = null;
}
if (dimens == null) {
attachmentStream = PartAuthority.getAttachmentStream(context, uri);
dimens = BitmapUtil.getDimensions(attachmentStream);
}
} catch (FileNotFoundException e) {
Log.w(TAG, "Failed to find file when retrieving media dimensions.", e);
} catch (IOException e) {
Log.w(TAG, "Experienced a read error when retrieving media dimensions.", e);
} catch (BitmapDecodingException e) {
Log.w(TAG, "Bitmap decoding error when retrieving dimensions.", e);
} finally {
if (attachmentStream != null) {
try {
attachmentStream.close();
} catch (IOException e) {
Log.w(TAG, "Failed to close stream after retrieving dimensions.", e);
}
}
}
}
if (dimens == null) {
dimens = new Pair<>(0, 0);
}
Log.d(TAG, "Dimensions for [" + uri + "] are " + dimens.first + " x " + dimens.second);
return dimens;
}
public static boolean isMms(String contentType) {
return !TextUtils.isEmpty(contentType) && contentType.trim().equals("application/mms");
}