mirror of
				https://github.com/oxen-io/session-android.git
				synced 2025-10-31 16:49:23 +00:00 
			
		
		
		
	Support for tiling image view and large image viewing
Fixes #5949 Fixes #5574 Fixes #4380 // FREEBIE
This commit is contained in:
		| @@ -5,7 +5,7 @@ | |||||||
|       android:versionCode="222" |       android:versionCode="222" | ||||||
|       android:versionName="3.25.4"> |       android:versionName="3.25.4"> | ||||||
|  |  | ||||||
|     <uses-sdk tools:overrideLibrary="com.amulyakhare.textdrawable,com.astuetz.pagerslidingtabstrip,pl.tajchert.waitingdots,com.h6ah4i.android.multiselectlistpreferencecompat,android.support.v13"/> |     <uses-sdk tools:overrideLibrary="com.amulyakhare.textdrawable,com.astuetz.pagerslidingtabstrip,pl.tajchert.waitingdots,com.h6ah4i.android.multiselectlistpreferencecompat,android.support.v13,com.davemorrissey.labs.subscaleview"/> | ||||||
|  |  | ||||||
|     <permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS" |     <permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS" | ||||||
|                 android:label="Access to TextSecure Secrets" |                 android:label="Access to TextSecure Secrets" | ||||||
|   | |||||||
| @@ -80,6 +80,9 @@ dependencies { | |||||||
|     compile 'org.whispersystems:signal-service-android:2.4.4' |     compile 'org.whispersystems:signal-service-android:2.4.4' | ||||||
|     compile 'com.h6ah4i.android.compat:mulsellistprefcompat:1.0.0' |     compile 'com.h6ah4i.android.compat:mulsellistprefcompat:1.0.0' | ||||||
|     compile 'com.google.zxing:core:3.2.1' |     compile 'com.google.zxing:core:3.2.1' | ||||||
|  |     compile ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') { | ||||||
|  |         exclude group: 'com.android.support', module: 'support-annotations' | ||||||
|  |     } | ||||||
|  |  | ||||||
|     compile ('cn.carbswang.android:NumberPickerView:1.0.9') { |     compile ('cn.carbswang.android:NumberPickerView:1.0.9') { | ||||||
|         exclude group: 'com.android.support', module: 'appcompat-v7' |         exclude group: 'com.android.support', module: 'appcompat-v7' | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								res/layout/zooming_image_view.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								res/layout/zooming_image_view.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <merge xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |        xmlns:tools="http://schemas.android.com/tools" | ||||||
|  |        tools:context="org.thoughtcrime.securesms.components.ZoomingImageView"> | ||||||
|  |  | ||||||
|  |     <ImageView android:id="@+id/image_view" | ||||||
|  |                android:layout_width="match_parent" | ||||||
|  |                android:layout_height="match_parent" | ||||||
|  |                android:visibility="visible"/> | ||||||
|  |  | ||||||
|  |     <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView | ||||||
|  |             android:id="@+id/subsampling_image_view" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="match_parent" | ||||||
|  |             android:visibility="gone"/> | ||||||
|  |  | ||||||
|  | </merge> | ||||||
| @@ -61,14 +61,15 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im | |||||||
|  |  | ||||||
|   private MasterSecret masterSecret; |   private MasterSecret masterSecret; | ||||||
|  |  | ||||||
|   private ZoomingImageView  image; |   private ZoomingImageView image; | ||||||
|   private VideoPlayer       video; |   private VideoPlayer      video; | ||||||
|   private Uri               mediaUri; |  | ||||||
|   private String            mediaType; |   private Uri       mediaUri; | ||||||
|   private Recipient         recipient; |   private String    mediaType; | ||||||
|   private long              threadId; |   private Recipient recipient; | ||||||
|   private long              date; |   private long      threadId; | ||||||
|   private long              size; |   private long      date; | ||||||
|  |   private long      size; | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   protected void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) { |   protected void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) { | ||||||
| @@ -137,8 +138,8 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void initializeViews() { |   private void initializeViews() { | ||||||
|     image = (ZoomingImageView)findViewById(R.id.image); |     image = (ZoomingImageView) findViewById(R.id.image); | ||||||
|     video = (VideoPlayer)findViewById(R.id.video_player); |     video = (VideoPlayer) findViewById(R.id.video_player); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void initializeResources() { |   private void initializeResources() { | ||||||
| @@ -171,7 +172,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im | |||||||
|       if (mediaType != null && mediaType.startsWith("image/")) { |       if (mediaType != null && mediaType.startsWith("image/")) { | ||||||
|         image.setVisibility(View.VISIBLE); |         image.setVisibility(View.VISIBLE); | ||||||
|         video.setVisibility(View.GONE); |         video.setVisibility(View.GONE); | ||||||
|         image.setImageUri(masterSecret, mediaUri); |         image.setImageUri(masterSecret, mediaUri, mediaType); | ||||||
|       } else if (mediaType != null && mediaType.startsWith("video/")) { |       } else if (mediaType != null && mediaType.startsWith("video/")) { | ||||||
|         image.setVisibility(View.GONE); |         image.setVisibility(View.GONE); | ||||||
|         video.setVisibility(View.VISIBLE); |         video.setVisibility(View.VISIBLE); | ||||||
| @@ -185,7 +186,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void cleanupMedia() { |   private void cleanupMedia() { | ||||||
|     image.setImageDrawable(null); |     image.cleanup(); | ||||||
|     video.cleanup(); |     video.cleanup(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,48 +1,131 @@ | |||||||
| package org.thoughtcrime.securesms.components; | package org.thoughtcrime.securesms.components; | ||||||
|  |  | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.graphics.Bitmap; | import android.graphics.Canvas; | ||||||
| import android.net.Uri; | import android.net.Uri; | ||||||
|  | import android.os.AsyncTask; | ||||||
|  | import android.support.annotation.Nullable; | ||||||
| import android.util.AttributeSet; | import android.util.AttributeSet; | ||||||
|  | import android.util.Log; | ||||||
|  | import android.util.Pair; | ||||||
|  | import android.view.View; | ||||||
|  | import android.widget.FrameLayout; | ||||||
| import android.widget.ImageView; | import android.widget.ImageView; | ||||||
|  |  | ||||||
| import com.bumptech.glide.Glide; | import com.bumptech.glide.Glide; | ||||||
| import com.bumptech.glide.load.engine.DiskCacheStrategy; | import com.bumptech.glide.load.engine.DiskCacheStrategy; | ||||||
| import com.bumptech.glide.load.resource.drawable.GlideDrawable; | import com.bumptech.glide.load.resource.drawable.GlideDrawable; | ||||||
| import com.bumptech.glide.request.target.BitmapImageViewTarget; | import com.bumptech.glide.request.RequestListener; | ||||||
| import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; | import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; | ||||||
|  | import com.bumptech.glide.request.target.Target; | ||||||
|  | import com.davemorrissey.labs.subscaleview.ImageSource; | ||||||
|  | import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; | ||||||
|  |  | ||||||
|  | import org.thoughtcrime.securesms.R; | ||||||
|  | import org.thoughtcrime.securesms.components.subsampling.AttachmentBitmapDecoder; | ||||||
|  | import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder; | ||||||
| import org.thoughtcrime.securesms.crypto.MasterSecret; | import org.thoughtcrime.securesms.crypto.MasterSecret; | ||||||
| import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; | import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; | ||||||
|  | import org.thoughtcrime.securesms.mms.PartAuthority; | ||||||
|  | import org.thoughtcrime.securesms.util.BitmapDecodingException; | ||||||
|  | import org.thoughtcrime.securesms.util.BitmapUtil; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  |  | ||||||
| import uk.co.senab.photoview.PhotoViewAttacher; | import uk.co.senab.photoview.PhotoViewAttacher; | ||||||
|  |  | ||||||
| public class ZoomingImageView extends ImageView { | public class ZoomingImageView extends FrameLayout { | ||||||
|   private PhotoViewAttacher attacher = new PhotoViewAttacher(this); |  | ||||||
|  |   private static final String TAG = ZoomingImageView.class.getName(); | ||||||
|  |  | ||||||
|  |   private final ImageView                 imageView; | ||||||
|  |   private final PhotoViewAttacher         imageViewAttacher; | ||||||
|  |   private final SubsamplingScaleImageView subsamplingImageView; | ||||||
|  |  | ||||||
|   public ZoomingImageView(Context context) { |   public ZoomingImageView(Context context) { | ||||||
|     super(context); |     this(context, null); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public ZoomingImageView(Context context, AttributeSet attrs) { |   public ZoomingImageView(Context context, AttributeSet attrs) { | ||||||
|     super(context, attrs); |     this(context, attrs, 0); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public ZoomingImageView(Context context, AttributeSet attrs, int defStyleAttr) { |   public ZoomingImageView(Context context, AttributeSet attrs, int defStyleAttr) { | ||||||
|     super(context, attrs, defStyleAttr); |     super(context, attrs, defStyleAttr); | ||||||
|  |  | ||||||
|  |     inflate(context, R.layout.zooming_image_view, this); | ||||||
|  |  | ||||||
|  |     this.imageView            = (ImageView) findViewById(R.id.image_view); | ||||||
|  |     this.subsamplingImageView = (SubsamplingScaleImageView) findViewById(R.id.subsampling_image_view); | ||||||
|  |     this.imageViewAttacher     = new PhotoViewAttacher(imageView); | ||||||
|  |  | ||||||
|  |     this.subsamplingImageView.setBitmapDecoderClass(AttachmentBitmapDecoder.class); | ||||||
|  |     this.subsamplingImageView.setRegionDecoderClass(AttachmentRegionDecoder.class); | ||||||
|  |     this.subsamplingImageView.setOrientation(SubsamplingScaleImageView.ORIENTATION_USE_EXIF); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public void setImageUri(MasterSecret masterSecret, Uri uri) { |   public void setImageUri(final MasterSecret masterSecret, final Uri uri, final String contentType) { | ||||||
|  |     final Context context        = getContext(); | ||||||
|  |     final int     maxTextureSize = BitmapUtil.getMaxTextureSize(); | ||||||
|  |  | ||||||
|  |     Log.w(TAG, "Max texture size: " + maxTextureSize); | ||||||
|  |  | ||||||
|  |     new AsyncTask<Void, Void, Pair<Integer, Integer>>() { | ||||||
|  |       @Override | ||||||
|  |       protected @Nullable Pair<Integer, Integer> doInBackground(Void... params) { | ||||||
|  |         if (contentType.equals("image/gif")) return null; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |           InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri); | ||||||
|  |           return BitmapUtil.getDimensions(inputStream); | ||||||
|  |         } catch (IOException | BitmapDecodingException e) { | ||||||
|  |           Log.w(TAG, e); | ||||||
|  |           return null; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       protected void onPostExecute(@Nullable Pair<Integer, Integer> dimensions) { | ||||||
|  |         Log.w(TAG, "Dimensions: " + dimensions.first + ", " + dimensions.second); | ||||||
|  |  | ||||||
|  |         if (dimensions == null || (dimensions.first <= maxTextureSize && dimensions.second <= maxTextureSize)) { | ||||||
|  |           Log.w(TAG, "Loading in standard image view..."); | ||||||
|  |           setImageViewUri(masterSecret, uri); | ||||||
|  |         } else { | ||||||
|  |           Log.w(TAG, "Loading in subsampling image view..."); | ||||||
|  |           setSubsamplingImageViewUri(uri); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }.execute(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private void setImageViewUri(MasterSecret masterSecret, Uri uri) { | ||||||
|  |     subsamplingImageView.setVisibility(View.GONE); | ||||||
|  |     imageView.setVisibility(View.VISIBLE); | ||||||
|  |  | ||||||
|     Glide.with(getContext()) |     Glide.with(getContext()) | ||||||
|          .load(new DecryptableUri(masterSecret, uri)) |          .load(new DecryptableUri(masterSecret, uri)) | ||||||
|          .diskCacheStrategy(DiskCacheStrategy.NONE) |          .diskCacheStrategy(DiskCacheStrategy.NONE) | ||||||
|          .dontTransform() |          .dontTransform() | ||||||
|          .dontAnimate() |          .dontAnimate() | ||||||
|          .into(new GlideDrawableImageViewTarget(this) { |          .into(new GlideDrawableImageViewTarget(imageView) { | ||||||
|            @Override protected void setResource(GlideDrawable resource) { |            @Override protected void setResource(GlideDrawable resource) { | ||||||
|              super.setResource(resource); |              super.setResource(resource); | ||||||
|              attacher.update(); |              imageViewAttacher.update(); | ||||||
|            } |            } | ||||||
|          }); |          }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private void setSubsamplingImageViewUri(Uri uri) { | ||||||
|  |     subsamplingImageView.setVisibility(View.VISIBLE); | ||||||
|  |     imageView.setVisibility(View.GONE); | ||||||
|  |  | ||||||
|  |     subsamplingImageView.setImage(ImageSource.uri(uri)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   public void cleanup() { | ||||||
|  |     imageView.setImageDrawable(null); | ||||||
|  |     subsamplingImageView.recycle(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | package org.thoughtcrime.securesms.components.subsampling; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import android.content.Context; | ||||||
|  | import android.graphics.Bitmap; | ||||||
|  | import android.graphics.BitmapFactory; | ||||||
|  | import android.graphics.Rect; | ||||||
|  | import android.net.Uri; | ||||||
|  |  | ||||||
|  | import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder; | ||||||
|  | import com.davemorrissey.labs.subscaleview.decoder.SkiaImageDecoder; | ||||||
|  |  | ||||||
|  | import org.thoughtcrime.securesms.crypto.MasterSecret; | ||||||
|  | import org.thoughtcrime.securesms.mms.PartAuthority; | ||||||
|  | import org.thoughtcrime.securesms.service.KeyCachingService; | ||||||
|  |  | ||||||
|  | import java.io.InputStream; | ||||||
|  |  | ||||||
|  | public class AttachmentBitmapDecoder implements ImageDecoder{ | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public Bitmap decode(Context context, Uri uri) throws Exception { | ||||||
|  |     if (!PartAuthority.isLocalUri(uri)) { | ||||||
|  |       return new SkiaImageDecoder().decode(context, uri); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     MasterSecret masterSecret = KeyCachingService.getMasterSecret(context); | ||||||
|  |  | ||||||
|  |     if (masterSecret == null) { | ||||||
|  |       throw new IllegalStateException("Can't decode without secret"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri); | ||||||
|  |  | ||||||
|  |     try { | ||||||
|  |       BitmapFactory.Options options = new BitmapFactory.Options(); | ||||||
|  |       options.inPreferredConfig = Bitmap.Config.ARGB_8888; | ||||||
|  |  | ||||||
|  |       Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options); | ||||||
|  |  | ||||||
|  |       if (bitmap == null) { | ||||||
|  |         throw new RuntimeException("Skia image region decoder returned null bitmap - image format may not be supported"); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return bitmap; | ||||||
|  |     } finally { | ||||||
|  |       if (inputStream != null) inputStream.close(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,95 @@ | |||||||
|  | package org.thoughtcrime.securesms.components.subsampling; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import android.content.Context; | ||||||
|  | import android.graphics.Bitmap; | ||||||
|  | import android.graphics.BitmapFactory; | ||||||
|  | import android.graphics.BitmapRegionDecoder; | ||||||
|  | import android.graphics.Point; | ||||||
|  | import android.graphics.Rect; | ||||||
|  | import android.net.Uri; | ||||||
|  | import android.os.Build; | ||||||
|  | import android.support.annotation.RequiresApi; | ||||||
|  | import android.util.Log; | ||||||
|  |  | ||||||
|  | import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder; | ||||||
|  | import com.davemorrissey.labs.subscaleview.decoder.SkiaImageRegionDecoder; | ||||||
|  |  | ||||||
|  | import org.thoughtcrime.securesms.crypto.MasterSecret; | ||||||
|  | import org.thoughtcrime.securesms.mms.PartAuthority; | ||||||
|  | import org.thoughtcrime.securesms.service.KeyCachingService; | ||||||
|  |  | ||||||
|  | import java.io.InputStream; | ||||||
|  |  | ||||||
|  | public class AttachmentRegionDecoder implements ImageRegionDecoder { | ||||||
|  |  | ||||||
|  |   private static final String TAG = AttachmentRegionDecoder.class.getName(); | ||||||
|  |  | ||||||
|  |   private SkiaImageRegionDecoder passthrough; | ||||||
|  |  | ||||||
|  |   private BitmapRegionDecoder bitmapRegionDecoder; | ||||||
|  |  | ||||||
|  |   @RequiresApi(api = Build.VERSION_CODES.GINGERBREAD_MR1) | ||||||
|  |   @Override | ||||||
|  |   public Point init(Context context, Uri uri) throws Exception { | ||||||
|  |     Log.w(TAG, "Init!"); | ||||||
|  |     if (!PartAuthority.isLocalUri(uri)) { | ||||||
|  |       passthrough = new SkiaImageRegionDecoder(); | ||||||
|  |       return passthrough.init(context, uri); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     MasterSecret masterSecret = KeyCachingService.getMasterSecret(context); | ||||||
|  |  | ||||||
|  |     if (masterSecret == null) { | ||||||
|  |       throw new IllegalStateException("No master secret available..."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri); | ||||||
|  |  | ||||||
|  |     this.bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); | ||||||
|  |     inputStream.close(); | ||||||
|  |  | ||||||
|  |     return new Point(bitmapRegionDecoder.getWidth(), bitmapRegionDecoder.getHeight()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @RequiresApi(api = Build.VERSION_CODES.GINGERBREAD_MR1) | ||||||
|  |   @Override | ||||||
|  |   public Bitmap decodeRegion(Rect rect, int sampleSize) { | ||||||
|  |     Log.w(TAG, "Decode region: " + rect); | ||||||
|  |  | ||||||
|  |     if (passthrough != null) { | ||||||
|  |       return passthrough.decodeRegion(rect, sampleSize); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     synchronized(this) { | ||||||
|  |       BitmapFactory.Options options = new BitmapFactory.Options(); | ||||||
|  |       options.inSampleSize      = sampleSize; | ||||||
|  |       options.inPreferredConfig = Bitmap.Config.RGB_565; | ||||||
|  |  | ||||||
|  |       Bitmap bitmap = bitmapRegionDecoder.decodeRegion(rect, options); | ||||||
|  |  | ||||||
|  |       if (bitmap == null) { | ||||||
|  |         throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported"); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return bitmap; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @RequiresApi(api = Build.VERSION_CODES.GINGERBREAD_MR1) | ||||||
|  |   public boolean isReady() { | ||||||
|  |     Log.w(TAG, "isReady"); | ||||||
|  |     return (passthrough != null && passthrough.isReady()) || | ||||||
|  |            (bitmapRegionDecoder != null && !bitmapRegionDecoder.isRecycled()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @RequiresApi(api = Build.VERSION_CODES.GINGERBREAD_MR1) | ||||||
|  |   public void recycle() { | ||||||
|  |     if (passthrough != null) { | ||||||
|  |       passthrough.recycle(); | ||||||
|  |       passthrough = null; | ||||||
|  |     } else { | ||||||
|  |       bitmapRegionDecoder.recycle(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -332,6 +332,7 @@ public class AttachmentManager { | |||||||
|     if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { |     if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { | ||||||
|       Intent intent = new Intent(context, MediaPreviewActivity.class); |       Intent intent = new Intent(context, MediaPreviewActivity.class); | ||||||
|       intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |       intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | ||||||
|  |       intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize()); | ||||||
|       intent.setDataAndType(slide.getUri(), slide.getContentType()); |       intent.setDataAndType(slide.getUri(), slide.getContentType()); | ||||||
|  |  | ||||||
|       context.startActivity(intent); |       context.startActivity(intent); | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ public abstract class MediaConstraints { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public boolean isWithinBounds(Context context, MasterSecret masterSecret, Uri uri) throws IOException { |   private boolean isWithinBounds(Context context, MasterSecret masterSecret, Uri uri) throws IOException { | ||||||
|     try { |     try { | ||||||
|       InputStream is = PartAuthority.getAttachmentStream(context, masterSecret, uri); |       InputStream is = PartAuthority.getAttachmentStream(context, masterSecret, uri); | ||||||
|       Pair<Integer, Integer> dimensions = BitmapUtil.getDimensions(is); |       Pair<Integer, Integer> dimensions = BitmapUtil.getDimensions(is); | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ import org.thoughtcrime.securesms.util.Util; | |||||||
|  |  | ||||||
| public class PushMediaConstraints extends MediaConstraints { | public class PushMediaConstraints extends MediaConstraints { | ||||||
|   private static final int MAX_IMAGE_DIMEN_LOWMEM = 768; |   private static final int MAX_IMAGE_DIMEN_LOWMEM = 768; | ||||||
|   private static final int MAX_IMAGE_DIMEN        = 2048; |   private static final int MAX_IMAGE_DIMEN        = 4096; | ||||||
|   private static final int KB                     = 1024; |   private static final int KB                     = 1024; | ||||||
|   private static final int MB                     = 1024 * KB; |   private static final int MB                     = 1024 * KB; | ||||||
|  |  | ||||||
| @@ -22,12 +22,12 @@ public class PushMediaConstraints extends MediaConstraints { | |||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   public int getImageMaxSize() { |   public int getImageMaxSize() { | ||||||
|     return 4 * MB; |     return 6 * MB; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   public int getGifMaxSize() { |   public int getGifMaxSize() { | ||||||
|     return 5 * MB; |     return 6 * MB; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Override |   @Override | ||||||
|   | |||||||
| @@ -32,6 +32,11 @@ import java.io.IOException; | |||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.util.concurrent.atomic.AtomicBoolean; | import java.util.concurrent.atomic.AtomicBoolean; | ||||||
|  |  | ||||||
|  | import javax.microedition.khronos.egl.EGL10; | ||||||
|  | import javax.microedition.khronos.egl.EGLConfig; | ||||||
|  | import javax.microedition.khronos.egl.EGLContext; | ||||||
|  | import javax.microedition.khronos.egl.EGLDisplay; | ||||||
|  |  | ||||||
| public class BitmapUtil { | public class BitmapUtil { | ||||||
|  |  | ||||||
|   private static final String TAG = BitmapUtil.class.getSimpleName(); |   private static final String TAG = BitmapUtil.class.getSimpleName(); | ||||||
| @@ -47,10 +52,13 @@ public class BitmapUtil { | |||||||
|     int    quality  = MAX_COMPRESSION_QUALITY; |     int    quality  = MAX_COMPRESSION_QUALITY; | ||||||
|     int    attempts = 0; |     int    attempts = 0; | ||||||
|     byte[] bytes; |     byte[] bytes; | ||||||
|     Bitmap scaledBitmap = createScaledBitmap(context, |  | ||||||
|                                              model, |     Bitmap scaledBitmap =  Downsampler.AT_MOST.decode(getInputStreamForModel(context, model), | ||||||
|                                              constraints.getImageMaxWidth(context), |                                                       Glide.get(context).getBitmapPool(), | ||||||
|                                              constraints.getImageMaxHeight(context)); |                                                       constraints.getImageMaxWidth(context), | ||||||
|  |                                                       constraints.getImageMaxHeight(context), | ||||||
|  |                                                       DecodeFormat.PREFER_RGB_565); | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       do { |       do { | ||||||
|         ByteArrayOutputStream baos = new ByteArrayOutputStream(); |         ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||||||
| @@ -307,4 +315,34 @@ public class BitmapUtil { | |||||||
|       return result[0]; |       return result[0]; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public static int getMaxTextureSize() { | ||||||
|  |     final int IMAGE_MAX_BITMAP_DIMENSION = 2048; | ||||||
|  |  | ||||||
|  |     EGL10 egl = (EGL10) EGLContext.getEGL(); | ||||||
|  |     EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); | ||||||
|  |  | ||||||
|  |     int[] version = new int[2]; | ||||||
|  |     egl.eglInitialize(display, version); | ||||||
|  |  | ||||||
|  |     int[] totalConfigurations = new int[1]; | ||||||
|  |     egl.eglGetConfigs(display, null, 0, totalConfigurations); | ||||||
|  |  | ||||||
|  |     EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]]; | ||||||
|  |     egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations); | ||||||
|  |  | ||||||
|  |     int[] textureSize = new int[1]; | ||||||
|  |     int maximumTextureSize = 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < totalConfigurations[0]; i++) { | ||||||
|  |       egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize); | ||||||
|  |  | ||||||
|  |       if (maximumTextureSize < textureSize[0]) | ||||||
|  |         maximumTextureSize = textureSize[0]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     egl.eglTerminate(display); | ||||||
|  |  | ||||||
|  |     return Math.max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,6 +9,8 @@ import android.text.TextUtils; | |||||||
| import android.util.Log; | import android.util.Log; | ||||||
| import android.webkit.MimeTypeMap; | import android.webkit.MimeTypeMap; | ||||||
|  |  | ||||||
|  | import com.bumptech.glide.Glide; | ||||||
|  |  | ||||||
| import org.thoughtcrime.securesms.R; | import org.thoughtcrime.securesms.R; | ||||||
| import org.thoughtcrime.securesms.attachments.Attachment; | import org.thoughtcrime.securesms.attachments.Attachment; | ||||||
| import org.thoughtcrime.securesms.crypto.MasterSecret; | import org.thoughtcrime.securesms.crypto.MasterSecret; | ||||||
| @@ -23,6 +25,7 @@ import org.thoughtcrime.securesms.providers.PersistentBlobProvider; | |||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
|  | import java.util.concurrent.ExecutionException; | ||||||
|  |  | ||||||
| import ws.com.google.android.mms.ContentType; | import ws.com.google.android.mms.ContentType; | ||||||
|  |  | ||||||
| @@ -52,8 +55,18 @@ public class MediaUtil { | |||||||
|   private static Bitmap generateImageThumbnail(Context context, MasterSecret masterSecret, Uri uri) |   private static Bitmap generateImageThumbnail(Context context, MasterSecret masterSecret, Uri uri) | ||||||
|       throws BitmapDecodingException |       throws BitmapDecodingException | ||||||
|   { |   { | ||||||
|     int maxSize = context.getResources().getDimensionPixelSize(R.dimen.media_bubble_height); |     try { | ||||||
|     return BitmapUtil.createScaledBitmap(context, new DecryptableUri(masterSecret, uri), maxSize, maxSize); |       int maxSize = context.getResources().getDimensionPixelSize(R.dimen.media_bubble_height); | ||||||
|  |       return Glide.with(context) | ||||||
|  |                   .load(new DecryptableUri(masterSecret, uri)) | ||||||
|  |                   .asBitmap() | ||||||
|  |                   .centerCrop() | ||||||
|  |                   .into(maxSize, maxSize) | ||||||
|  |                   .get(); | ||||||
|  |     } catch (InterruptedException | ExecutionException e) { | ||||||
|  |       Log.w(TAG, e); | ||||||
|  |       throw new BitmapDecodingException(e); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public static Slide getSlideForAttachment(Context context, Attachment attachment) { |   public static Slide getSlideForAttachment(Context context, Attachment attachment) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Moxie Marlinspike
					Moxie Marlinspike