ditch RoundedImageView, make animated gifs work

// FREEBIE
This commit is contained in:
Jake McGinty 2015-07-15 13:42:59 -07:00 committed by Moxie Marlinspike
parent a66dd8be82
commit f13ad54ba1
19 changed files with 231 additions and 66 deletions

View File

@ -44,7 +44,7 @@ dependencies {
compile 'org.w3c:smil:1.0.0'
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
compile 'com.github.chrisbanes.photoview:library:1.2.3'
compile 'com.github.bumptech.glide:glide:3.6.0'
compile 'com.github.bumptech.glide:glide:3.6.1'
compile 'com.makeramen:roundedimageview:2.1.0'
compile 'com.pnikosis:materialish-progress:1.5'
compile 'de.greenrobot:eventbus:2.4.0'
@ -108,7 +108,7 @@ dependencyVerification {
'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b',
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
'com.github.chrisbanes.photoview:library:8b5344e206f125e7ba9d684008f36c4992d03853c57e5814125f88496126e3cc',
'com.github.bumptech.glide:glide:adf657e6bddccb168a29e18ab0954043af46a9b5c736d8c3193c9783fd83d69e',
'com.github.bumptech.glide:glide:4718ac4c57ebabe56e673dc3265950b9dbf940d1c43c0adc363e8b95c0abdf75',
'com.makeramen:roundedimageview:1f5a1865796b308c6cdd114acc6e78408b110f0a62fc63553278fbeacd489cd1',
'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54',
'de.greenrobot:eventbus:61d743a748156a372024d083de763b9e91ac2dcb3f6a1cbc74995c7ddab6e968',

View File

@ -55,8 +55,8 @@
android:id="@+id/attachment_thumbnail"
android:layout_width="230dp"
android:layout_height="150dp"
app:riv_corner_radius="3dp"
android:contentDescription="@string/conversation_activity__attachment_thumbnail"/>
android:contentDescription="@string/conversation_activity__attachment_thumbnail"
app:backgroundColorHint="?conversation_background" />
</FrameLayout>
<ImageView android:id="@+id/remove_image_button"

View File

@ -54,8 +54,6 @@
android:adjustViewBounds="true"
android:contentDescription="@string/conversation_item__mms_image_description"
android:visibility="gone"
app:riv_corner_radius="@dimen/message_bubble_corner_radius"
app:riv_border_width="0dp"
tools:src="@drawable/ic_video_light"
tools:visibility="visible" />

View File

@ -65,8 +65,6 @@
android:adjustViewBounds="true"
android:contentDescription="@string/conversation_item__mms_image_description"
android:visibility="gone"
app:riv_corner_radius="@dimen/message_bubble_corner_radius"
app:riv_border_width="0dp"
tools:src="@drawable/ic_video_light"
tools:visibility="visible" />

View File

@ -25,7 +25,6 @@
android:layout_width="70dp"
android:layout_height="70dp"
position="bottom_right"
app:riv_oval="true"
android:layout_marginRight="10dp"
android:src="@drawable/ic_group_photo"
android:contentDescription="@string/GroupCreateActivity_avatar_content_description" />

View File

@ -2,15 +2,13 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/thumbnail_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:contentDescription="@string/conversation_item__mms_image_description"
android:layout_margin="@dimen/media_bubble_border_width"
app:riv_corner_radius="@dimen/message_bubble_corner_radius" />
<ImageView android:id="@+id/thumbnail_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:contentDescription="@string/conversation_item__mms_image_description"
android:layout_margin="@dimen/media_bubble_border_width" />
<com.pnikosis.materialishprogress.ProgressWheel
android:id="@+id/progress_wheel"

View File

@ -130,4 +130,7 @@
<attr name="inverted" format="boolean"/>
</declare-styleable>
<declare-styleable name="ThumbnailView">
<attr name="backgroundColorHint" format="color" />
</declare-styleable>
</resources>

View File

@ -218,11 +218,12 @@ public class ConversationItem extends LinearLayout implements Recipient.Recipien
private void setBubbleState(MessageRecord messageRecord, Recipient recipient) {
if (messageRecord.isOutgoing()) {
bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY);
mediaThumbnail.setBackgroundColorHint(defaultBubbleColor);
} else {
bodyBubble.getBackground().setColorFilter(recipient.getColor().toConversationColor(context),
PorterDuff.Mode.MULTIPLY);
int color = recipient.getColor().toConversationColor(context);
bodyBubble.getBackground().setColorFilter(color, PorterDuff.Mode.MULTIPLY);
mediaThumbnail.setBackgroundColorHint(color);
}
}
private void setSelectionBackgroundDrawables(MessageRecord messageRecord) {

View File

@ -7,11 +7,9 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.makeramen.roundedimageview.RoundedImageView;
import org.thoughtcrime.securesms.R;
public class ImageDivet extends RoundedImageView {
public class ImageDivet extends ImageView {
private static final float CORNER_OFFSET = 12F;
private static final String[] POSITIONS = new String[] {"bottom_right"};

View File

@ -3,7 +3,8 @@ package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.support.annotation.NonNull;
@ -15,13 +16,16 @@ import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.bumptech.glide.DrawableTypeRequest;
import com.bumptech.glide.GenericRequestBuilder;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.makeramen.roundedimageview.RoundedImageView;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.R;
@ -32,6 +36,7 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.mms.RoundedCorners;
import org.thoughtcrime.securesms.util.Util;
import de.greenrobot.event.EventBus;
@ -40,9 +45,11 @@ import ws.com.google.android.mms.pdu.PduPart;
public class ThumbnailView extends FrameLayout {
private static final String TAG = ThumbnailView.class.getSimpleName();
private boolean showProgress = true;
private RoundedImageView image;
private ProgressWheel progress;
private boolean showProgress = true;
private ImageView image;
private ProgressWheel progress;
private int backgroundColorHint;
private int radius;
private ListenableFutureTask<SlideDeck> slideDeckFuture = null;
private SlideDeckListener slideDeckListener = null;
@ -61,8 +68,15 @@ public class ThumbnailView extends FrameLayout {
public ThumbnailView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
inflate(context, R.layout.thumbnail_view, this);
image = (RoundedImageView) findViewById(R.id.thumbnail_image);
progress = (ProgressWheel) findViewById(R.id.progress_wheel);
radius = getResources().getDimensionPixelSize(R.dimen.message_bubble_corner_radius);
image = (ImageView) findViewById(R.id.thumbnail_image);
progress = (ProgressWheel) findViewById(R.id.progress_wheel);
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0);
backgroundColorHint = typedArray.getColor(0, Color.BLACK);
typedArray.recycle();
}
}
@Override protected void onAttachedToWindow() {
@ -80,13 +94,17 @@ public class ThumbnailView extends FrameLayout {
if (this.slide != null && event.partId.equals(this.slide.getPart().getPartId())) {
Util.runOnMain(new Runnable() {
@Override public void run() {
progress.setInstantProgress(((float) event.progress) / event.total);
progress.setInstantProgress(((float)event.progress) / event.total);
if (event.progress >= event.total) animateOutProgress();
}
});
}
}
public void setBackgroundColorHint(int color) {
this.backgroundColorHint = color;
}
public void setImageResource(@Nullable MasterSecret masterSecret,
long id, long timestamp,
@NonNull ListenableFutureTask<SlideDeck> slideDeckFuture)
@ -155,6 +173,7 @@ public class ThumbnailView extends FrameLayout {
private GenericRequestBuilder buildGlideRequest(@NonNull Slide slide,
@Nullable MasterSecret masterSecret)
{
Log.w(TAG, "slide type " + slide.getContentType());
final GenericRequestBuilder builder;
if (slide.getThumbnailUri() != null) {
builder = buildThumbnailGlideRequest(slide, masterSecret);
@ -182,8 +201,7 @@ public class ThumbnailView extends FrameLayout {
if (masterSecret == null) request = Glide.with(getContext()).load(slide.getThumbnailUri());
else request = Glide.with(getContext()).load(new DecryptableUri(masterSecret, slide.getThumbnailUri()));
return request.asBitmap()
.fitCenter()
return request.transform(new RoundedCorners(getContext(), false, radius, backgroundColorHint))
.listener(new PduThumbnailSetListener(slide.getPart()));
}
@ -192,9 +210,9 @@ public class ThumbnailView extends FrameLayout {
throw new IllegalStateException("null MasterSecret when loading non-draft thumbnail");
}
return Glide.with(getContext()).load(new DecryptableUri(masterSecret, slide.getThumbnailUri()))
.asBitmap()
.centerCrop();
return Glide.with(getContext()).load(new DecryptableUri(masterSecret, slide.getThumbnailUri()))
.crossFade()
.transform(new RoundedCorners(getContext(), true, radius, backgroundColorHint));
}
private GenericRequestBuilder buildPlaceholderGlideRequest(Slide slide) {
@ -282,7 +300,7 @@ public class ThumbnailView extends FrameLayout {
}
}
private static class PduThumbnailSetListener implements RequestListener<Object, Bitmap> {
private static class PduThumbnailSetListener implements RequestListener<Object, GlideDrawable> {
private PduPart part;
public PduThumbnailSetListener(@NonNull PduPart part) {
@ -290,15 +308,17 @@ public class ThumbnailView extends FrameLayout {
}
@Override
public boolean onException(Exception e, Object model, Target<Bitmap> target, boolean isFirstResource) {
public boolean onException(Exception e, Object model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
part.setThumbnail(resource);
public boolean onResourceReady(GlideDrawable resource, Object model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
if (resource instanceof GlideBitmapDrawable) {
Log.w(TAG, "onResourceReady() for a Bitmap. Saving.");
part.setThumbnail(((GlideBitmapDrawable)resource).getBitmap());
}
return false;
}
}
}

View File

@ -7,7 +7,9 @@ import android.util.AttributeSet;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
@ -32,11 +34,10 @@ public class ZoomingImageView extends ImageView {
public void setImageUri(MasterSecret masterSecret, Uri uri) {
Glide.with(getContext())
.load(new DecryptableUri(masterSecret, uri))
.asBitmap()
.dontTransform()
.dontAnimate()
.into(new BitmapImageViewTarget(this) {
@Override protected void setResource(Bitmap resource) {
.into(new GlideDrawableImageViewTarget(this) {
@Override protected void setResource(GlideDrawable resource) {
super.setResource(resource);
attacher.update();
}

View File

@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.providers.CaptureProvider;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.MediaUtil;
import java.io.IOException;
@ -68,22 +69,14 @@ public class AttachmentManager {
AlphaAnimation animation = new AlphaAnimation(1.0f, 0.0f);
animation.setDuration(200);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
@Override public void onAnimationStart(Animation animation) {}
@Override public void onAnimationRepeat(Animation animation) {}
@Override public void onAnimationEnd(Animation animation) {
slideDeck.clear();
attachmentView.setVisibility(View.GONE);
attachmentListener.onAttachmentChanged();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
attachmentView.startAnimation(animation);
@ -95,7 +88,11 @@ public class AttachmentManager {
}
public void setImage(MasterSecret masterSecret, Uri image) throws IOException, BitmapDecodingException {
setMedia(new ImageSlide(context, masterSecret, image), masterSecret);
if (MediaUtil.getMimeType(context, image).startsWith("image/gif")) {
setMedia(new GifSlide(context, masterSecret, image), masterSecret);
} else {
setMedia(new ImageSlide(context, masterSecret, image), masterSecret);
}
}
public void setVideo(Uri video) throws IOException, MediaTooLargeException {
@ -148,7 +145,6 @@ public class AttachmentManager {
return captureUri;
}
public void setCaptureUri(Uri captureUri) {
this.captureUri = captureUri;
}

View File

@ -0,0 +1,27 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.net.Uri;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import java.io.IOException;
import ws.com.google.android.mms.pdu.PduPart;
public class GifSlide extends ImageSlide {
public GifSlide(Context context, MasterSecret masterSecret, PduPart part) {
super(context, masterSecret, part);
}
public GifSlide(Context context, MasterSecret masterSecret, Uri uri)
throws IOException, BitmapDecodingException
{
super(context, masterSecret, uri);
}
@Override public Uri getThumbnailUri() {
return getPart().getDataUri();
}
}

View File

@ -24,6 +24,7 @@ import android.support.annotation.DrawableRes;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.MediaUtil;
import java.io.IOException;
@ -38,7 +39,7 @@ public class ImageSlide extends Slide {
}
public ImageSlide(Context context, MasterSecret masterSecret, Uri uri) throws IOException, BitmapDecodingException {
super(context, masterSecret, constructPartFromUri(uri));
super(context, masterSecret, constructPartFromUri(context, uri));
}
@Override
@ -62,13 +63,15 @@ public class ImageSlide extends Slide {
return true;
}
private static PduPart constructPartFromUri(Uri uri)
private static PduPart constructPartFromUri(Context context, Uri uri)
throws IOException, BitmapDecodingException
{
PduPart part = new PduPart();
final String mimeType = MediaUtil.getMimeType(context, uri);
part.setDataUri(uri);
part.setContentType(ContentType.IMAGE_JPEG.getBytes());
part.setContentType((mimeType != null ? mimeType : ContentType.IMAGE_JPEG).getBytes());
part.setContentId((System.currentTimeMillis()+"").getBytes());
part.setName(("Image" + System.currentTimeMillis()).getBytes());

View File

@ -25,15 +25,18 @@ public abstract class MediaConstraints {
public abstract int getImageMaxHeight(Context context);
public abstract int getImageMaxSize();
public abstract int getGifMaxSize();
public abstract int getVideoMaxSize();
public abstract int getAudioMaxSize();
public boolean isSatisfied(Context context, MasterSecret masterSecret, PduPart part) {
try {
return (MediaUtil.isImage(part) && part.getDataSize() <= getImageMaxSize() && isWithinBounds(context, masterSecret, part.getDataUri())) ||
(MediaUtil.isAudio(part) && part.getDataSize() <= getAudioMaxSize()) ||
(MediaUtil.isVideo(part) && part.getDataSize() <= getVideoMaxSize()) ||
return (MediaUtil.isGif(part) && part.getDataSize() <= getGifMaxSize()) ||
(MediaUtil.isImage(part) && part.getDataSize() <= getImageMaxSize() && isWithinBounds(context, masterSecret, part.getDataUri())) ||
(MediaUtil.isAudio(part) && part.getDataSize() <= getAudioMaxSize()) ||
(MediaUtil.isVideo(part) && part.getDataSize() <= getVideoMaxSize()) ||
(!MediaUtil.isImage(part) && !MediaUtil.isAudio(part) && !MediaUtil.isVideo(part));
} catch (IOException ioe) {
Log.w(TAG, "Failed to determine if media's constraints are satisfied.", ioe);
@ -49,7 +52,7 @@ public abstract class MediaConstraints {
}
public boolean canResize(PduPart part) {
return part != null && MediaUtil.isImage(part);
return part != null && MediaUtil.isImage(part) && !MediaUtil.isGif(part);
}
public byte[] getResizedMedia(Context context, MasterSecret masterSecret, PduPart part)

View File

@ -24,6 +24,11 @@ public class MmsMediaConstraints extends MediaConstraints {
return MAX_MESSAGE_SIZE;
}
@Override
public int getGifMaxSize() {
return MAX_MESSAGE_SIZE;
}
@Override
public int getVideoMaxSize() {
return MAX_MESSAGE_SIZE;

View File

@ -25,6 +25,11 @@ public class PushMediaConstraints extends MediaConstraints {
return 420 * KB;
}
@Override
public int getGifMaxSize() {
return 1 * MB;
}
@Override
public int getVideoMaxSize() {
return MmsMediaConstraints.MAX_MESSAGE_SIZE;

View File

@ -0,0 +1,88 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
public class RoundedCorners extends BitmapTransformation {
private final boolean crop;
private final int radius;
private final int colorHint;
public RoundedCorners(@NonNull Context context, boolean crop, int radius, int colorHint) {
super(context);
this.crop = crop;
this.radius = radius;
this.colorHint = colorHint;
}
@Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth,
int outHeight)
{
final Bitmap toRound = crop ? centerCrop(pool, toTransform, outWidth, outHeight)
: fitCenter(pool, toTransform, outWidth, outHeight);
return round(pool, toRound);
}
private Bitmap centerCrop(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
final Bitmap toReuse = pool.get(outWidth, outHeight, getSafeConfig(toTransform));
final Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);
if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
toReuse.recycle();
}
return transformed;
}
private Bitmap fitCenter(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return TransformationUtils.fitCenter(toTransform, pool, outWidth, outHeight);
}
private Bitmap round(@NonNull BitmapPool pool, @Nullable Bitmap toRound) {
if (toRound == null) {
return null;
}
final Bitmap result;
final Bitmap toReuse = pool.get(toRound.getWidth(), toRound.getHeight(), getSafeConfig(toRound));
if (toReuse != null) {
result = toReuse;
} else {
result = Bitmap.createBitmap(toRound.getWidth(), toRound.getHeight(), getSafeConfig(toRound));
}
final Canvas canvas = new Canvas(result);
final Paint cornerPaint = new Paint();
final Paint shaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
shaderPaint.setShader(new BitmapShader(toRound, TileMode.CLAMP, TileMode.CLAMP));
cornerPaint.setColor(colorHint);
if (Config.RGB_565.equals(result.getConfig())) {
canvas.drawRect(0, 0, radius, radius, cornerPaint);
canvas.drawRect(0, toRound.getHeight() - radius, radius, toRound.getHeight(), cornerPaint);
canvas.drawRect(toRound.getWidth() - radius, 0, toRound.getWidth(), radius, cornerPaint);
canvas.drawRect(toRound.getWidth() - radius, toRound.getHeight() - radius, toRound.getWidth(), toRound.getHeight(), cornerPaint);
}
canvas.drawRoundRect(new RectF(0, 0, toRound.getWidth(), toRound.getHeight()), radius, radius, shaderPaint);
// Log.w("RoundedCorners", "in was " + toRound.getWidth() + "x" + toRound.getHeight() + ", out to " + result.getWidth() + "x" + result.getHeight());
return result;
}
private static Bitmap.Config getSafeConfig(Bitmap bitmap) {
return bitmap.getConfig() != null ? bitmap.getConfig() : Bitmap.Config.ARGB_8888;
}
@Override public String getId() {
return RoundedCorners.class.getCanonicalName();
}
}

View File

@ -3,11 +3,14 @@ package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.mms.GifSlide;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.Slide;
@ -59,7 +62,9 @@ public class MediaUtil {
public static Slide getSlideForPart(Context context, MasterSecret masterSecret, PduPart part, String contentType) {
Slide slide = null;
if (ContentType.isImageType(contentType)) {
if (isGif(contentType)) {
slide = new GifSlide(context, masterSecret, part);
} else if (ContentType.isImageType(contentType)) {
slide = new ImageSlide(context, masterSecret, part);
} else if (ContentType.isVideoType(contentType)) {
slide = new VideoSlide(context, masterSecret, part);
@ -70,6 +75,23 @@ public class MediaUtil {
return slide;
}
public static String getMimeType(Context context, Uri uri) {
String type = context.getContentResolver().getType(uri);
if (type == null) {
final String extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString());
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}
private static boolean isGif(String contentType) {
return !TextUtils.isEmpty(contentType) && contentType.trim().equals("image/gif");
}
public static boolean isGif(PduPart part) {
return isGif(Util.toIsoString(part.getContentType()));
}
public static boolean isImage(PduPart part) {
return ContentType.isImageType(Util.toIsoString(part.getContentType()));
}