fix image preview rotation in ComposeActivity (#831)
This commit is contained in:
parent
0b3bee0d15
commit
aac63441d7
3 changed files with 111 additions and 91 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue