fix image preview rotation in ComposeActivity (#831)

This commit is contained in:
Konrad Pozniak 2018-09-03 20:26:35 +02:00 committed by GitHub
parent 0b3bee0d15
commit aac63441d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 91 deletions

View file

@ -18,6 +18,7 @@ package com.keylesspalace.tusky;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.arch.lifecycle.Lifecycle;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -135,12 +136,20 @@ import java.util.Locale;
import javax.inject.Inject; import javax.inject.Inject;
import at.connyduck.sparkbutton.helpers.Utils; import at.connyduck.sparkbutton.helpers.Utils;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
import static com.uber.autodispose.AutoDispose.autoDisposable;
import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from;
public final class ComposeActivity public final class ComposeActivity
extends BaseActivity extends BaseActivity
implements ComposeOptionsListener, implements ComposeOptionsListener,
@ -1121,11 +1130,24 @@ public final class ComposeActivity
DisplayMetrics displayMetrics = new DisplayMetrics(); DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
Picasso.with(this) Single.fromCallable(() ->
.load(item.uri) MediaUtils.getSampledBitmap(getContentResolver(), item.uri, displayMetrics.widthPixels, displayMetrics.heightPixels))
.resize(displayMetrics.widthPixels, displayMetrics.heightPixels) .subscribeOn(Schedulers.computation())
.onlyScaleDown() .observeOn(AndroidSchedulers.mainThread())
.into(imageView); .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(new SingleObserver<Bitmap>() {
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onSuccess(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
}
@Override
public void onError(Throwable e) { }
});
int margin = Utils.dpToPx(this, 4); int margin = Utils.dpToPx(this, 4);
dialogLayout.addView(imageView); dialogLayout.addView(imageView);

View file

@ -18,16 +18,11 @@ package com.keylesspalace.tusky.util;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.support.media.ExifInterface;
import android.util.Log;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -37,7 +32,6 @@ import java.util.List;
* aspect ratio and orientation. * aspect ratio and orientation.
*/ */
public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> { public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
private static final String TAG = "DownsizeImageTask";
private int sizeLimit; private int sizeLimit;
private ContentResolver contentResolver; private ContentResolver contentResolver;
private Listener listener; private Listener listener;
@ -54,83 +48,6 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
this.listener = listener; this.listener = listener;
} }
@Nullable
private static Bitmap reorientBitmap(Bitmap bitmap, int orientation) {
Matrix matrix = new Matrix();
switch (orientation) {
default:
case ExifInterface.ORIENTATION_NORMAL: {
return bitmap;
}
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: {
matrix.setScale(-1, 1);
break;
}
case ExifInterface.ORIENTATION_ROTATE_180: {
matrix.setRotate(180);
break;
}
case ExifInterface.ORIENTATION_FLIP_VERTICAL: {
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
}
case ExifInterface.ORIENTATION_TRANSPOSE: {
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
}
case ExifInterface.ORIENTATION_ROTATE_90: {
matrix.setRotate(90);
break;
}
case ExifInterface.ORIENTATION_TRANSVERSE: {
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
}
case ExifInterface.ORIENTATION_ROTATE_270: {
matrix.setRotate(-90);
break;
}
}
try {
Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
if (!bitmap.sameAs(result)) {
bitmap.recycle();
}
return result;
} catch (OutOfMemoryError e) {
return null;
}
}
private static int getOrientation(Uri uri, ContentResolver contentResolver) {
InputStream inputStream;
try {
inputStream = contentResolver.openInputStream(uri);
} catch (FileNotFoundException e) {
Log.d(TAG, Log.getStackTraceString(e));
return ExifInterface.ORIENTATION_UNDEFINED;
}
if (inputStream == null) {
return ExifInterface.ORIENTATION_UNDEFINED;
}
ExifInterface exifInterface;
try {
exifInterface = new ExifInterface(inputStream);
} catch (IOException e) {
Log.d(TAG, Log.getStackTraceString(e));
IOUtils.closeQuietly(inputStream);
return ExifInterface.ORIENTATION_UNDEFINED;
}
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
IOUtils.closeQuietly(inputStream);
return orientation;
}
@Override @Override
protected Boolean doInBackground(Uri... uris) { protected Boolean doInBackground(Uri... uris) {
resultList = new ArrayList<>(); resultList = new ArrayList<>();
@ -147,7 +64,7 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
BitmapFactory.decodeStream(inputStream, null, options); BitmapFactory.decodeStream(inputStream, null, options);
IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(inputStream);
// Get EXIF data, for orientation info. // Get EXIF data, for orientation info.
int orientation = getOrientation(uri, contentResolver); int orientation = MediaUtils.getImageOrientation(uri, contentResolver);
// Then use that information to determine how much to compress. // Then use that information to determine how much to compress.
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
/* Unfortunately, there isn't a determined worst case compression ratio for image /* Unfortunately, there isn't a determined worst case compression ratio for image
@ -176,7 +93,7 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
if (scaledBitmap == null) { if (scaledBitmap == null) {
return false; return false;
} }
Bitmap reorientedBitmap = reorientBitmap(scaledBitmap, orientation); Bitmap reorientedBitmap = MediaUtils.reorientBitmap(scaledBitmap, orientation);
if (reorientedBitmap == null) { if (reorientedBitmap == null) {
scaledBitmap.recycle(); scaledBitmap.recycle();
return false; return false;

View file

@ -20,6 +20,7 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.MediaMetadataRetriever; import android.media.MediaMetadataRetriever;
import android.media.ThumbnailUtils; import android.media.ThumbnailUtils;
import android.net.Uri; import android.net.Uri;
@ -27,6 +28,7 @@ import android.provider.OpenableColumns;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.Px; import android.support.annotation.Px;
import android.support.media.ExifInterface;
import android.util.Log; import android.util.Log;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -111,7 +113,9 @@ public class MediaUtils {
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
try { try {
stream = contentResolver.openInputStream(uri); stream = contentResolver.openInputStream(uri);
return BitmapFactory.decodeStream(stream, null, options); Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
int orientation = getImageOrientation(uri, contentResolver);
return reorientBitmap(bitmap, orientation);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Log.w(TAG, e); Log.w(TAG, e);
return null; return null;
@ -176,4 +180,81 @@ public class MediaUtils {
return inSampleSize; return inSampleSize;
} }
@Nullable
public static Bitmap reorientBitmap(Bitmap bitmap, int orientation) {
Matrix matrix = new Matrix();
switch (orientation) {
default:
case ExifInterface.ORIENTATION_NORMAL: {
return bitmap;
}
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: {
matrix.setScale(-1, 1);
break;
}
case ExifInterface.ORIENTATION_ROTATE_180: {
matrix.setRotate(180);
break;
}
case ExifInterface.ORIENTATION_FLIP_VERTICAL: {
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
}
case ExifInterface.ORIENTATION_TRANSPOSE: {
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
}
case ExifInterface.ORIENTATION_ROTATE_90: {
matrix.setRotate(90);
break;
}
case ExifInterface.ORIENTATION_TRANSVERSE: {
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
}
case ExifInterface.ORIENTATION_ROTATE_270: {
matrix.setRotate(-90);
break;
}
}
try {
Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
if (!bitmap.sameAs(result)) {
bitmap.recycle();
}
return result;
} catch (OutOfMemoryError e) {
return null;
}
}
public static int getImageOrientation(Uri uri, ContentResolver contentResolver) {
InputStream inputStream;
try {
inputStream = contentResolver.openInputStream(uri);
} catch (FileNotFoundException e) {
Log.w(TAG, e);
return ExifInterface.ORIENTATION_UNDEFINED;
}
if (inputStream == null) {
return ExifInterface.ORIENTATION_UNDEFINED;
}
ExifInterface exifInterface;
try {
exifInterface = new ExifInterface(inputStream);
} catch (IOException e) {
Log.w(TAG, e);
IOUtils.closeQuietly(inputStream);
return ExifInterface.ORIENTATION_UNDEFINED;
}
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
IOUtils.closeQuietly(inputStream);
return orientation;
}
} }