2011-12-20 18:20:44 +00:00
|
|
|
/**
|
|
|
|
* Copyright (C) 2011 Whisper Systems
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
package org.thoughtcrime.securesms.mms;
|
|
|
|
|
2013-04-26 18:23:43 +00:00
|
|
|
import android.content.Context;
|
2014-12-17 19:47:19 +00:00
|
|
|
import android.graphics.Bitmap;
|
|
|
|
import android.graphics.BitmapFactory;
|
2013-04-26 18:23:43 +00:00
|
|
|
import android.graphics.Color;
|
2013-09-09 01:19:05 +00:00
|
|
|
import android.graphics.drawable.AnimationDrawable;
|
2013-04-26 18:23:43 +00:00
|
|
|
import android.graphics.drawable.BitmapDrawable;
|
|
|
|
import android.graphics.drawable.ColorDrawable;
|
|
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
import android.graphics.drawable.TransitionDrawable;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.Handler;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.widget.ImageView;
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.R;
|
|
|
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
2013-05-21 17:32:48 +00:00
|
|
|
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
2013-04-26 18:23:43 +00:00
|
|
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
2013-05-06 19:22:03 +00:00
|
|
|
import org.thoughtcrime.securesms.util.LRUCache;
|
2014-06-28 21:06:15 +00:00
|
|
|
import org.thoughtcrime.securesms.util.SmilUtil;
|
|
|
|
import org.w3c.dom.smil.SMILDocument;
|
|
|
|
import org.w3c.dom.smil.SMILMediaElement;
|
|
|
|
import org.w3c.dom.smil.SMILRegionElement;
|
2014-11-03 23:16:04 +00:00
|
|
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
2013-04-26 18:23:43 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.lang.ref.SoftReference;
|
2013-04-26 18:23:43 +00:00
|
|
|
import java.lang.ref.WeakReference;
|
2013-05-06 19:22:03 +00:00
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Map;
|
2011-12-20 18:20:44 +00:00
|
|
|
|
|
|
|
import ws.com.google.android.mms.ContentType;
|
|
|
|
import ws.com.google.android.mms.pdu.PduPart;
|
|
|
|
|
|
|
|
public class ImageSlide extends Slide {
|
2014-12-17 19:47:19 +00:00
|
|
|
private static final String TAG = ImageSlide.class.getSimpleName();
|
2011-12-20 18:20:44 +00:00
|
|
|
|
|
|
|
private static final int MAX_CACHE_SIZE = 10;
|
2013-09-09 01:19:05 +00:00
|
|
|
private static final Map<Uri, SoftReference<Drawable>> thumbnailCache =
|
|
|
|
Collections.synchronizedMap(new LRUCache<Uri, SoftReference<Drawable>>(MAX_CACHE_SIZE));
|
2011-12-20 18:20:44 +00:00
|
|
|
|
|
|
|
public ImageSlide(Context context, MasterSecret masterSecret, PduPart part) {
|
|
|
|
super(context, masterSecret, part);
|
|
|
|
}
|
|
|
|
|
2014-08-18 12:45:31 +00:00
|
|
|
public ImageSlide(Context context, MasterSecret masterSecret, Uri uri) throws IOException, BitmapDecodingException {
|
|
|
|
super(context, constructPartFromUri(context, masterSecret, uri));
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
2014-06-28 21:06:15 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
@Override
|
2013-09-09 01:19:05 +00:00
|
|
|
public Drawable getThumbnail(int maxWidth, int maxHeight) {
|
|
|
|
Drawable thumbnail = getCachedThumbnail();
|
2013-04-26 18:23:43 +00:00
|
|
|
|
2013-09-09 01:19:05 +00:00
|
|
|
if (thumbnail != null) {
|
2013-04-26 18:23:43 +00:00
|
|
|
return thumbnail;
|
2013-09-09 01:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (part.isPendingPush()) {
|
|
|
|
return context.getResources().getDrawable(R.drawable.stat_sys_download);
|
|
|
|
}
|
2011-12-20 18:20:44 +00:00
|
|
|
|
|
|
|
try {
|
2014-12-17 19:47:19 +00:00
|
|
|
Bitmap thumbnailBitmap;
|
|
|
|
long startDecode = System.currentTimeMillis();
|
|
|
|
Log.w(TAG, (part.getThumbnailUri() == null ? "generating" : "fetching pre-generated") + " thumbnail");
|
|
|
|
if (part.getThumbnailUri() != null) thumbnailBitmap = BitmapFactory.decodeStream(PartAuthority.getPartStream(context, masterSecret, part.getThumbnailUri()));
|
|
|
|
else thumbnailBitmap = BitmapUtil.createScaledBitmap(context, masterSecret, getUri(), maxWidth, maxHeight);
|
|
|
|
|
|
|
|
Log.w(TAG, "thumbnail decode/generate time: " + (System.currentTimeMillis() - startDecode) + "ms");
|
|
|
|
|
|
|
|
thumbnail = new BitmapDrawable(context.getResources(), thumbnailBitmap);
|
2014-08-18 12:45:31 +00:00
|
|
|
thumbnailCache.put(part.getDataUri(), new SoftReference<>(thumbnail));
|
2011-12-20 18:20:44 +00:00
|
|
|
|
|
|
|
return thumbnail;
|
2013-04-26 18:23:43 +00:00
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
Log.w("ImageSlide", e);
|
2013-09-09 01:19:05 +00:00
|
|
|
return context.getResources().getDrawable(R.drawable.ic_missing_thumbnail_picture);
|
2013-05-21 17:32:48 +00:00
|
|
|
} catch (BitmapDecodingException e) {
|
|
|
|
Log.w("ImageSlide", e);
|
2013-09-09 01:19:05 +00:00
|
|
|
return context.getResources().getDrawable(R.drawable.ic_missing_thumbnail_picture);
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-26 18:23:43 +00:00
|
|
|
@Override
|
|
|
|
public void setThumbnailOn(ImageView imageView) {
|
2013-09-09 01:19:05 +00:00
|
|
|
Drawable thumbnail = getCachedThumbnail();
|
2013-04-26 18:23:43 +00:00
|
|
|
|
|
|
|
if (thumbnail != null) {
|
|
|
|
Log.w("ImageSlide", "Setting cached thumbnail...");
|
|
|
|
setThumbnailOn(imageView, thumbnail, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
final ColorDrawable temporaryDrawable = new ColorDrawable(Color.TRANSPARENT);
|
|
|
|
final WeakReference<ImageView> weakImageView = new WeakReference<ImageView>(imageView);
|
|
|
|
final Handler handler = new Handler();
|
|
|
|
final int maxWidth = imageView.getWidth();
|
|
|
|
final int maxHeight = imageView.getHeight();
|
|
|
|
|
|
|
|
imageView.setImageDrawable(temporaryDrawable);
|
|
|
|
|
2013-06-21 18:56:59 +00:00
|
|
|
if (maxWidth == 0 || maxHeight == 0)
|
|
|
|
return;
|
|
|
|
|
2013-04-26 18:23:43 +00:00
|
|
|
MmsDatabase.slideResolver.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2014-12-17 19:47:19 +00:00
|
|
|
final Drawable bitmap = getThumbnail(maxWidth, maxHeight);
|
2013-04-26 18:23:43 +00:00
|
|
|
final ImageView destination = weakImageView.get();
|
2013-09-09 01:19:05 +00:00
|
|
|
|
2013-04-26 18:23:43 +00:00
|
|
|
if (destination != null && destination.getDrawable() == temporaryDrawable) {
|
|
|
|
handler.post(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
setThumbnailOn(destination, bitmap, false);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
2013-04-26 18:23:43 +00:00
|
|
|
|
2013-09-09 01:19:05 +00:00
|
|
|
private void setThumbnailOn(ImageView imageView, Drawable thumbnail, boolean fromMemory) {
|
2013-04-26 18:23:43 +00:00
|
|
|
if (fromMemory) {
|
2013-09-09 01:19:05 +00:00
|
|
|
imageView.setImageDrawable(thumbnail);
|
|
|
|
} else if (thumbnail instanceof AnimationDrawable) {
|
|
|
|
imageView.setImageDrawable(thumbnail);
|
|
|
|
((AnimationDrawable)imageView.getDrawable()).start();
|
2013-04-26 18:23:43 +00:00
|
|
|
} else {
|
2013-09-09 01:19:05 +00:00
|
|
|
TransitionDrawable fadingResult = new TransitionDrawable(new Drawable[]{new ColorDrawable(Color.TRANSPARENT), thumbnail});
|
2013-04-26 18:23:43 +00:00
|
|
|
imageView.setImageDrawable(fadingResult);
|
|
|
|
fadingResult.startTransition(300);
|
|
|
|
}
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
2013-04-26 18:23:43 +00:00
|
|
|
|
2013-09-09 01:19:05 +00:00
|
|
|
private Drawable getCachedThumbnail() {
|
2013-04-26 18:23:43 +00:00
|
|
|
synchronized (thumbnailCache) {
|
2013-09-09 01:19:05 +00:00
|
|
|
SoftReference<Drawable> bitmapReference = thumbnailCache.get(part.getDataUri());
|
2013-04-26 18:23:43 +00:00
|
|
|
Log.w("ImageSlide", "Got soft reference: " + bitmapReference);
|
|
|
|
|
|
|
|
if (bitmapReference != null) {
|
2013-09-09 01:19:05 +00:00
|
|
|
Drawable bitmap = bitmapReference.get();
|
2013-04-26 18:23:43 +00:00
|
|
|
Log.w("ImageSlide", "Got cached bitmap: " + bitmap);
|
|
|
|
if (bitmap != null) return bitmap;
|
|
|
|
else thumbnailCache.remove(part.getDataUri());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
@Override
|
2013-04-26 18:23:43 +00:00
|
|
|
public boolean hasImage() {
|
2011-12-20 18:20:44 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-06-28 21:06:15 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public SMILRegionElement getSmilRegion(SMILDocument document) {
|
|
|
|
SMILRegionElement region = (SMILRegionElement) document.createElement("region");
|
|
|
|
region.setId("Image");
|
|
|
|
region.setLeft(0);
|
|
|
|
region.setTop(0);
|
|
|
|
region.setWidth(SmilUtil.ROOT_WIDTH);
|
|
|
|
region.setHeight(SmilUtil.ROOT_HEIGHT);
|
|
|
|
region.setFit("meet");
|
|
|
|
return region;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public SMILMediaElement getMediaElement(SMILDocument document) {
|
|
|
|
return SmilUtil.createMediaElement("img", document, new String(getPart().getName()));
|
|
|
|
}
|
|
|
|
|
2014-08-18 12:45:31 +00:00
|
|
|
private static PduPart constructPartFromUri(Context context, MasterSecret masterSecret, Uri uri)
|
2013-05-21 17:32:48 +00:00
|
|
|
throws IOException, BitmapDecodingException
|
|
|
|
{
|
2011-12-20 18:20:44 +00:00
|
|
|
PduPart part = new PduPart();
|
2014-08-18 12:45:31 +00:00
|
|
|
byte[] data = BitmapUtil.createScaledBytes(context, masterSecret, uri, 1280, 1280, MAX_MESSAGE_SIZE);
|
2013-04-26 18:23:43 +00:00
|
|
|
|
|
|
|
part.setData(data);
|
2011-12-20 18:20:44 +00:00
|
|
|
part.setDataUri(uri);
|
|
|
|
part.setContentType(ContentType.IMAGE_JPEG.getBytes());
|
|
|
|
part.setContentId((System.currentTimeMillis()+"").getBytes());
|
|
|
|
part.setName(("Image" + System.currentTimeMillis()).getBytes());
|
|
|
|
|
|
|
|
return part;
|
|
|
|
}
|
|
|
|
}
|