mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-24 01:21:09 +00:00
committed by
Moxie Marlinspike
parent
9ba19df2af
commit
f42d100f15
@@ -20,21 +20,17 @@ import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Pair;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -43,14 +39,14 @@ public class AttachmentManager {
|
||||
|
||||
private final Context context;
|
||||
private final View attachmentView;
|
||||
private final ImageView thumbnail;
|
||||
private final ThumbnailView thumbnail;
|
||||
private final Button removeButton;
|
||||
private final SlideDeck slideDeck;
|
||||
private final AttachmentListener attachmentListener;
|
||||
|
||||
public AttachmentManager(Activity view, AttachmentListener listener) {
|
||||
this.attachmentView = (View)view.findViewById(R.id.attachment_editor);
|
||||
this.thumbnail = (ImageView)view.findViewById(R.id.attachment_thumbnail);
|
||||
this.thumbnail = (ThumbnailView)view.findViewById(R.id.attachment_thumbnail);
|
||||
this.removeButton = (Button)view.findViewById(R.id.remove_image_button);
|
||||
this.slideDeck = new SlideDeck();
|
||||
this.context = view;
|
||||
@@ -66,7 +62,7 @@ public class AttachmentManager {
|
||||
}
|
||||
|
||||
public void setImage(Uri image) throws IOException, BitmapDecodingException {
|
||||
setMedia(new ImageSlide(context, image), 345, 261);
|
||||
setMedia(new ImageSlide(context, image));
|
||||
}
|
||||
|
||||
public void setVideo(Uri video) throws IOException, MediaTooLargeException {
|
||||
@@ -77,32 +73,11 @@ public class AttachmentManager {
|
||||
setMedia(new AudioSlide(context, audio));
|
||||
}
|
||||
|
||||
public void setMedia(final Slide slide, final int thumbnailWidth, final int thumbnailHeight) {
|
||||
public void setMedia(final Slide slide) {
|
||||
slideDeck.clear();
|
||||
slideDeck.addSlide(slide);
|
||||
slide.getThumbnail(context).addListener(new FutureTaskListener<Pair<Drawable, Boolean>>() {
|
||||
@Override
|
||||
public void onSuccess(final Pair<Drawable, Boolean> result) {
|
||||
thumbnail.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
thumbnail.setImageDrawable(result.first);
|
||||
attachmentView.setVisibility(View.VISIBLE);
|
||||
attachmentListener.onAttachmentChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
Log.w(TAG, error);
|
||||
slideDeck.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setMedia(Slide slide) {
|
||||
setMedia(slide, thumbnail.getWidth(), thumbnail.getHeight());
|
||||
attachmentView.setVisibility(View.VISIBLE);
|
||||
thumbnail.setImageResource(slide);
|
||||
}
|
||||
|
||||
public boolean isAttachmentPresent() {
|
||||
|
||||
@@ -16,20 +16,20 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import java.io.IOException;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore.Audio;
|
||||
import android.support.annotation.DrawableRes;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.ResUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore.Audio;
|
||||
import android.util.Pair;
|
||||
|
||||
public class AudioSlide extends Slide {
|
||||
|
||||
@@ -52,8 +52,8 @@ public class AudioSlide extends Slide {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFutureTask<Pair<Drawable,Boolean>> getThumbnail(Context context) {
|
||||
return new ListenableFutureTask<>(new Pair<>(ResUtil.getDrawable(context, R.attr.conversation_icon_attach_audio), true));
|
||||
public @DrawableRes int getPlaceholderRes(Theme theme) {
|
||||
return ResUtil.getDrawableRes(theme, R.attr.conversation_icon_attach_audio);
|
||||
}
|
||||
|
||||
public static PduPart constructPartFromUri(Context context, Uri uri) throws IOException, MediaTooLargeException {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bumptech.glide.load.data.StreamLocalUriFetcher;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class DecryptableStreamLocalUriFetcher extends StreamLocalUriFetcher {
|
||||
private static final String TAG = DecryptableStreamLocalUriFetcher.class.getSimpleName();
|
||||
private Context context;
|
||||
private MasterSecret masterSecret;
|
||||
|
||||
public DecryptableStreamLocalUriFetcher(Context context, MasterSecret masterSecret, Uri uri) {
|
||||
super(context, uri);
|
||||
this.context = context;
|
||||
this.masterSecret = masterSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream loadResource(Uri uri, ContentResolver contentResolver) throws FileNotFoundException {
|
||||
try {
|
||||
return PartAuthority.getPartStream(context, masterSecret, uri);
|
||||
} catch (IOException ioe) {
|
||||
Log.w(TAG, ioe);
|
||||
throw new FileNotFoundException("PartAuthority couldn't load Uri resource.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.bumptech.glide.load.data.DataFetcher;
|
||||
import com.bumptech.glide.load.model.GenericLoaderFactory;
|
||||
import com.bumptech.glide.load.model.ModelLoader;
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||
import com.bumptech.glide.load.model.stream.StreamModelLoader;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A {@link ModelLoader} for translating uri models into {@link InputStream} data. Capable of handling 'http',
|
||||
* 'https', 'android.resource', 'content', and 'file' schemes. Unsupported schemes will throw an exception in
|
||||
* {@link #getResourceFetcher(Uri, int, int)}.
|
||||
*/
|
||||
public class DecryptableStreamUriLoader implements StreamModelLoader<DecryptableUri> {
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
* THe default factory for {@link com.bumptech.glide.load.model.stream.StreamUriLoader}s.
|
||||
*/
|
||||
public static class Factory implements ModelLoaderFactory<DecryptableUri, InputStream> {
|
||||
|
||||
@Override
|
||||
public StreamModelLoader<DecryptableUri> build(Context context, GenericLoaderFactory factories) {
|
||||
return new DecryptableStreamUriLoader(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teardown() {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
public DecryptableStreamUriLoader(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFetcher<InputStream> getResourceFetcher(DecryptableUri model, int width, int height) {
|
||||
return new DecryptableStreamLocalUriFetcher(context, model.masterSecret, model.uri);
|
||||
}
|
||||
|
||||
public static class DecryptableUri {
|
||||
public MasterSecret masterSecret;
|
||||
public Uri uri;
|
||||
|
||||
public DecryptableUri(MasterSecret masterSecret, Uri uri) {
|
||||
this.masterSecret = masterSecret;
|
||||
this.uri = uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,27 +17,15 @@
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.support.annotation.DrawableRes;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.LRUCache;
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import ws.com.google.android.mms.ContentType;
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
@@ -45,10 +33,6 @@ import ws.com.google.android.mms.pdu.PduPart;
|
||||
public class ImageSlide extends Slide {
|
||||
private static final String TAG = ImageSlide.class.getSimpleName();
|
||||
|
||||
private static final int MAX_CACHE_SIZE = 10;
|
||||
private static final Map<Uri, SoftReference<Drawable>> thumbnailCache =
|
||||
Collections.synchronizedMap(new LRUCache<Uri, SoftReference<Drawable>>(MAX_CACHE_SIZE));
|
||||
|
||||
public ImageSlide(Context context, MasterSecret masterSecret, PduPart part) {
|
||||
super(context, masterSecret, part);
|
||||
}
|
||||
@@ -58,68 +42,21 @@ public class ImageSlide extends Slide {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFutureTask<Pair<Drawable,Boolean>> getThumbnail(Context context) {
|
||||
if (getPart().isPendingPush()) {
|
||||
return new ListenableFutureTask<>(new Pair<>(context.getResources().getDrawable(R.drawable.stat_sys_download), true));
|
||||
}
|
||||
|
||||
Drawable thumbnail = getCachedThumbnail();
|
||||
if (thumbnail != null) {
|
||||
Log.w(TAG, "getThumbnail() returning cached thumbnail");
|
||||
return new ListenableFutureTask<>(new Pair<>(thumbnail, true));
|
||||
}
|
||||
|
||||
Log.w(TAG, "getThumbnail() resolving thumbnail, as it wasn't cached");
|
||||
return resolveThumbnail(context);
|
||||
}
|
||||
|
||||
private ListenableFutureTask<Pair<Drawable,Boolean>> resolveThumbnail(Context context) {
|
||||
final WeakReference<Context> weakContext = new WeakReference<>(context);
|
||||
|
||||
Callable<Pair<Drawable,Boolean>> slideCallable = new Callable<Pair<Drawable, Boolean>>() {
|
||||
@Override
|
||||
public Pair<Drawable, Boolean> call() throws Exception {
|
||||
final Context context = weakContext.get();
|
||||
if (context == null) {
|
||||
Log.w(TAG, "context SoftReference was null, leaving");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final long startDecode = System.currentTimeMillis();
|
||||
final Bitmap thumbnailBitmap = MediaUtil.getOrGenerateThumbnail(context, masterSecret, part);
|
||||
final Drawable thumbnail = new BitmapDrawable(context.getResources(), thumbnailBitmap);
|
||||
Log.w(TAG, "thumbnail decode/generate time: " + (System.currentTimeMillis() - startDecode) + "ms");
|
||||
|
||||
thumbnailCache.put(part.getDataUri(), new SoftReference<>(thumbnail));
|
||||
return new Pair<>(thumbnail, false);
|
||||
} catch (IOException | BitmapDecodingException e) {
|
||||
Log.w(TAG, e);
|
||||
return new Pair<>(context.getResources().getDrawable(R.drawable.ic_missing_thumbnail_picture), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
ListenableFutureTask<Pair<Drawable,Boolean>> futureTask = new ListenableFutureTask<>(slideCallable);
|
||||
MmsDatabase.slideResolver.execute(futureTask);
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
private Drawable getCachedThumbnail() {
|
||||
synchronized (thumbnailCache) {
|
||||
SoftReference<Drawable> bitmapReference = thumbnailCache.get(part.getDataUri());
|
||||
Log.w("ImageSlide", "Got soft reference: " + bitmapReference);
|
||||
|
||||
if (bitmapReference != null) {
|
||||
Drawable bitmap = bitmapReference.get();
|
||||
Log.w("ImageSlide", "Got cached bitmap: " + bitmap);
|
||||
if (bitmap != null) return bitmap;
|
||||
else thumbnailCache.remove(part.getDataUri());
|
||||
}
|
||||
public Uri getThumbnailUri() {
|
||||
if (!getPart().isPendingPush() && getPart().getDataUri() != null) {
|
||||
return isDraft()
|
||||
? getPart().getDataUri()
|
||||
: PartAuthority.getThumbnailUri(getPart().getId());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @DrawableRes int getPlaceholderRes(Theme theme) {
|
||||
return R.drawable.ic_missing_thumbnail_picture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasImage() {
|
||||
return true;
|
||||
@@ -137,4 +74,5 @@ public class ImageSlide extends Slide {
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,16 +15,20 @@ import java.io.InputStream;
|
||||
|
||||
public class PartAuthority {
|
||||
|
||||
private static final String PART_URI_STRING = "content://org.thoughtcrime.securesms/part";
|
||||
public static final Uri PART_CONTENT_URI = Uri.parse(PART_URI_STRING);
|
||||
private static final String PART_URI_STRING = "content://org.thoughtcrime.securesms/part";
|
||||
private static final String THUMB_URI_STRING = "content://org.thoughtcrime.securesms/thumb";
|
||||
public static final Uri PART_CONTENT_URI = Uri.parse(PART_URI_STRING);
|
||||
public static final Uri THUMB_CONTENT_URI = Uri.parse(THUMB_URI_STRING);
|
||||
|
||||
private static final int PART_ROW = 1;
|
||||
private static final int THUMB_ROW = 2;
|
||||
|
||||
private static final UriMatcher uriMatcher;
|
||||
|
||||
static {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
uriMatcher.addURI("org.thoughtcrime.securesms", "part/#", PART_ROW);
|
||||
uriMatcher.addURI("org.thoughtcrime.securesms", "thumb/#", THUMB_ROW);
|
||||
}
|
||||
|
||||
public static InputStream getPartStream(Context context, MasterSecret masterSecret, Uri uri)
|
||||
@@ -36,6 +40,7 @@ public class PartAuthority {
|
||||
try {
|
||||
switch (match) {
|
||||
case PART_ROW: return partDatabase.getPartStream(masterSecret, ContentUris.parseId(uri));
|
||||
case THUMB_ROW: return partDatabase.getThumbnailStream(masterSecret, ContentUris.parseId(uri));
|
||||
default: return context.getContentResolver().openInputStream(uri);
|
||||
}
|
||||
} catch (SecurityException se) {
|
||||
@@ -43,19 +48,11 @@ public class PartAuthority {
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream getThumbnail(Context context, MasterSecret masterSecret, Uri uri)
|
||||
throws IOException
|
||||
{
|
||||
PartDatabase partDatabase = DatabaseFactory.getPartDatabase(context);
|
||||
int match = uriMatcher.match(uri);
|
||||
|
||||
switch (match) {
|
||||
case PART_ROW: return partDatabase.getThumbnailStream(masterSecret, ContentUris.parseId(uri));
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Uri getPublicPartUri(Uri uri) {
|
||||
return ContentUris.withAppendedId(PartProvider.CONTENT_URI, ContentUris.parseId(uri));
|
||||
}
|
||||
|
||||
public static Uri getThumbnailUri(long partId) {
|
||||
return ContentUris.withAppendedId(THUMB_CONTENT_URI, partId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,20 +16,18 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
|
||||
public abstract class Slide {
|
||||
@@ -68,10 +66,6 @@ public abstract class Slide {
|
||||
return part.getDataUri();
|
||||
}
|
||||
|
||||
public ListenableFutureTask<Pair<Drawable,Boolean>> getThumbnail(Context context) {
|
||||
throw new AssertionError("getThumbnail() called on non-thumbnail producing slide!");
|
||||
}
|
||||
|
||||
public boolean hasImage() {
|
||||
return false;
|
||||
}
|
||||
@@ -84,22 +78,22 @@ public abstract class Slide {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Bitmap getImage() {
|
||||
throw new AssertionError("getImage() called on non-image slide!");
|
||||
}
|
||||
|
||||
public boolean hasText() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
throw new AssertionError("getText() called on non-text slide!");
|
||||
}
|
||||
|
||||
public PduPart getPart() {
|
||||
return part;
|
||||
}
|
||||
|
||||
public Uri getThumbnailUri() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @DrawableRes int getPlaceholderRes(Theme theme) {
|
||||
throw new AssertionError("getPlaceholderRes() called for non-drawable slide");
|
||||
}
|
||||
|
||||
public boolean isDraft() {
|
||||
return getPart().getId() < 0;
|
||||
}
|
||||
|
||||
protected static void assertMediaSize(Context context, Uri uri)
|
||||
throws MediaTooLargeException, IOException
|
||||
{
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.GlideBuilder;
|
||||
import com.bumptech.glide.load.engine.cache.DiskCache;
|
||||
import com.bumptech.glide.load.engine.cache.DiskCacheAdapter;
|
||||
import com.bumptech.glide.module.GlideModule;
|
||||
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public class TextSecureGlideModule implements GlideModule {
|
||||
@Override
|
||||
public void applyOptions(Context context, GlideBuilder builder) {
|
||||
builder.setDiskCache(new NoopDiskCacheFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerComponents(Context context, Glide glide) {
|
||||
glide.register(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory());
|
||||
}
|
||||
|
||||
public static class NoopDiskCacheFactory implements DiskCache.Factory {
|
||||
@Override
|
||||
public DiskCache build() {
|
||||
return new DiskCacheAdapter();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,7 @@ package org.thoughtcrime.securesms.mms;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.thoughtcrime.securesms.util.SmilUtil;
|
||||
import org.w3c.dom.smil.SMILDocument;
|
||||
import org.w3c.dom.smil.SMILMediaElement;
|
||||
import org.w3c.dom.smil.SMILRegionElement;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.LRUCache;
|
||||
|
||||
@@ -51,35 +46,6 @@ public class TextSlide extends Slide {
|
||||
super(context, getPartForMessage(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasText() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
try {
|
||||
SoftReference<String> reference = textCache.get(part.getDataUri());
|
||||
|
||||
if (reference != null) {
|
||||
String cachedText = reference.get();
|
||||
|
||||
if (cachedText != null) {
|
||||
return cachedText;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String text = new String(getPartData(), CharacterSets.getMimeName(part.getCharset()));
|
||||
textCache.put(part.getDataUri(), new SoftReference<String>(text));
|
||||
|
||||
return text;
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
Log.w("TextSlide", uee);
|
||||
return new String(getPartData());
|
||||
}
|
||||
}
|
||||
|
||||
private static PduPart getPartForMessage(String message) {
|
||||
PduPart part = new PduPart();
|
||||
|
||||
|
||||
48
src/org/thoughtcrime/securesms/mms/ThumbnailTransform.java
Normal file
48
src/org/thoughtcrime/securesms/mms/ThumbnailTransform.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
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 ThumbnailTransform extends BitmapTransformation {
|
||||
private static final String TAG = ThumbnailTransform.class.getSimpleName();
|
||||
|
||||
public ThumbnailTransform(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public ThumbnailTransform(BitmapPool bitmapPool) {
|
||||
super(bitmapPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
|
||||
if (toTransform.getWidth() < (outWidth / 2) && toTransform.getHeight() < (outHeight / 2)) {
|
||||
return toTransform;
|
||||
}
|
||||
|
||||
final float inAspectRatio = (float) toTransform.getWidth() / toTransform.getHeight();
|
||||
final float outAspectRatio = (float) outWidth / outHeight;
|
||||
if (inAspectRatio < outAspectRatio) {
|
||||
outWidth = (int)(outHeight * inAspectRatio);
|
||||
}
|
||||
|
||||
final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null
|
||||
? toTransform.getConfig()
|
||||
: Bitmap.Config.ARGB_8888);
|
||||
Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);
|
||||
if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
|
||||
toReuse.recycle();
|
||||
}
|
||||
return transformed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ThumbnailTransform.class.getCanonicalName();
|
||||
}
|
||||
}
|
||||
@@ -16,22 +16,22 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import java.io.IOException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.ResUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
public class VideoSlide extends Slide {
|
||||
|
||||
@@ -44,8 +44,8 @@ public class VideoSlide extends Slide {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFutureTask<Pair<Drawable,Boolean>> getThumbnail(Context context) {
|
||||
return new ListenableFutureTask<>(new Pair<>(ResUtil.getDrawable(context, R.attr.conversation_icon_attach_video), true));
|
||||
public @DrawableRes int getPlaceholderRes(Theme theme) {
|
||||
return ResUtil.getDrawableRes(theme, R.attr.conversation_icon_attach_video);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user