com.yalantis.ucrop.task.BitmapLoadTask Maven / Gradle / Ivy
The newest version!
package com.yalantis.ucrop.task;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.luck.picture.lib.PictureContentResolver;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.tools.PictureFileUtils;
import com.luck.picture.lib.tools.SdkVersionUtils;
import com.yalantis.ucrop.callback.BitmapLoadCallback;
import com.yalantis.ucrop.model.ExifInfo;
import com.yalantis.ucrop.util.BitmapLoadUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
/**
* Creates and returns a Bitmap for a given Uri(String url).
* inSampleSize is calculated based on requiredWidth property. However can be adjusted if OOM occurs.
* If any EXIF config is found - bitmap is transformed properly.
*/
public class BitmapLoadTask extends AsyncTask {
private static final String TAG = "BitmapWorkerTask";
private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
private final Context mContext;
private Uri mInputUri;
private final Uri mOutputUri;
private final int mRequiredWidth;
private final int mRequiredHeight;
private final int mInputImageWidth;
private final int mInputImageHeight;
private final BitmapLoadCallback mBitmapLoadCallback;
public static class BitmapWorkerResult {
Bitmap mBitmapResult;
ExifInfo mExifInfo;
Exception mBitmapWorkerException;
public BitmapWorkerResult(@NonNull Bitmap bitmapResult, @NonNull ExifInfo exifInfo) {
mBitmapResult = bitmapResult;
mExifInfo = exifInfo;
}
public BitmapWorkerResult(@NonNull Exception bitmapWorkerException) {
mBitmapWorkerException = bitmapWorkerException;
}
}
public BitmapLoadTask(@NonNull Context context,
@NonNull Uri inputUri, @Nullable Uri outputUri,
int requiredWidth, int requiredHeight,
int imageWidth, int imageHeight,
BitmapLoadCallback loadCallback) {
mContext = context;
mInputUri = inputUri;
mOutputUri = outputUri;
mRequiredWidth = requiredWidth;
mRequiredHeight = requiredHeight;
mInputImageWidth = imageWidth;
mInputImageHeight = imageHeight;
mBitmapLoadCallback = loadCallback;
}
@Override
@NonNull
protected BitmapWorkerResult doInBackground(Void... params) {
if (mInputUri == null) {
return new BitmapWorkerResult(new NullPointerException("Input Uri cannot be null"));
}
try {
processInputUri();
} catch (NullPointerException | IOException e) {
return new BitmapWorkerResult(e);
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
if (options.outWidth == 0 && options.outHeight == 0) {
options.inSampleSize = BitmapLoadUtils.calculateInSampleSize(mInputImageWidth, mInputImageHeight, mInputImageWidth, mInputImageHeight);
} else {
options.inSampleSize = BitmapLoadUtils.calculateInSampleSize(options.outWidth, options.outHeight, mRequiredWidth, mRequiredHeight);
}
options.inJustDecodeBounds = false;
Bitmap decodeSampledBitmap = null;
boolean decodeAttemptSuccess = false;
while (!decodeAttemptSuccess) {
try {
InputStream stream = PictureContentResolver.getContentResolverOpenInputStream(mContext, mInputUri);
decodeSampledBitmap = BitmapFactory.decodeStream(stream, null, options);
if (options.outWidth == -1 || options.outHeight == -1) {
return new BitmapWorkerResult(new IllegalArgumentException("Bounds for bitmap could not be retrieved from the Uri: [" + mInputUri + "]"));
}
if (checkSize(decodeSampledBitmap, options)) continue;
decodeAttemptSuccess = true;
} catch (OutOfMemoryError error) {
Log.e(TAG, "doInBackground: BitmapFactory.decodeFileDescriptor: ", error);
options.inSampleSize *= 2;
} catch (Exception e) {
Log.e(TAG, "doInBackground: ImageDecoder.createSource: ", e);
return new BitmapWorkerResult(new IllegalArgumentException("Bitmap could not be decoded from the Uri: [" + mInputUri + "]", e));
}
}
if (decodeSampledBitmap == null) {
return new BitmapWorkerResult(new IllegalArgumentException("Bitmap could not be decoded from the Uri: [" + mInputUri + "]"));
}
int exifOrientation = BitmapLoadUtils.getExifOrientation(mContext, mInputUri);
int exifDegrees = BitmapLoadUtils.exifToDegrees(exifOrientation);
int exifTranslation = BitmapLoadUtils.exifToTranslation(exifOrientation);
ExifInfo exifInfo = new ExifInfo(exifOrientation, exifDegrees, exifTranslation);
Matrix matrix = new Matrix();
if (exifDegrees != 0) {
matrix.preRotate(exifDegrees);
}
if (exifTranslation != 1) {
matrix.postScale(exifTranslation, 1);
}
if (!matrix.isIdentity()) {
return new BitmapWorkerResult(BitmapLoadUtils.transformBitmap(decodeSampledBitmap, matrix), exifInfo);
}
return new BitmapWorkerResult(decodeSampledBitmap, exifInfo);
}
private void processInputUri() throws NullPointerException, IOException {
String inputUriScheme = mInputUri.getScheme();
Log.d(TAG, "Uri scheme: " + inputUriScheme);
if ("http".equals(inputUriScheme) || "https".equals(inputUriScheme)) {
try {
downloadFile(mInputUri, mOutputUri);
} catch (NullPointerException | IOException e) {
Log.e(TAG, "Downloading failed", e);
throw e;
}
} else if ("content".equals(inputUriScheme)) {
String path = getFilePath();
if (!TextUtils.isEmpty(path) && new File(path).exists()) {
mInputUri = SdkVersionUtils.isQ() ? mInputUri : Uri.fromFile(new File(path));
} else {
try {
copyFile(mInputUri, mOutputUri);
} catch (NullPointerException | IOException e) {
Log.e(TAG, "Copying failed", e);
throw e;
}
}
} else if (!"file".equals(inputUriScheme)) {
Log.e(TAG, "Invalid Uri scheme " + inputUriScheme);
throw new IllegalArgumentException("Invalid Uri scheme" + inputUriScheme);
}
}
private String getFilePath() {
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
return PictureFileUtils.getPath(mContext, mInputUri);
} else {
return "";
}
}
private void copyFile(@NonNull Uri inputUri, @Nullable Uri outputUri) throws NullPointerException, IOException {
Log.d(TAG, "copyFile");
if (outputUri == null) {
throw new NullPointerException("Output Uri is null - cannot copy image");
}
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = PictureContentResolver.getContentResolverOpenInputStream(mContext, inputUri);
outputStream = new FileOutputStream(outputUri.getPath());
if (inputStream == null) {
throw new NullPointerException("InputStream for given input Uri is null");
}
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
} finally {
BitmapLoadUtils.close(outputStream);
BitmapLoadUtils.close(inputStream);
// swap uris, because input image was copied to the output destination
// (cropped image will override it later)
mInputUri = mOutputUri;
}
}
private void downloadFile(@NonNull Uri inputUri, @Nullable Uri outputUri) throws NullPointerException, IOException {
Log.d(TAG, "downloadFile");
if (outputUri == null) {
throw new NullPointerException("Output Uri is null - cannot download image");
}
OutputStream outputStream = null;
BufferedInputStream bin = null;
BufferedOutputStream bout = null;
try {
URL u = new URL(inputUri.toString());
byte[] buffer = new byte[1024];
int read;
bin = new BufferedInputStream(u.openStream());
outputStream = PictureContentResolver.getContentResolverOpenOutputStream(mContext, outputUri);
if (outputStream != null) {
bout = new BufferedOutputStream(outputStream);
while ((read = bin.read(buffer)) > -1) {
bout.write(buffer, 0, read);
}
bout.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// swap uris, because input image was downloaded to the output destination
// (cropped image will override it later)
mInputUri = mOutputUri;
BitmapLoadUtils.close(bout);
BitmapLoadUtils.close(bin);
BitmapLoadUtils.close(outputStream);
}
}
@Override
protected void onPostExecute(@NonNull BitmapWorkerResult result) {
if (result.mBitmapWorkerException == null) {
String inputUriString = mInputUri.toString();
mBitmapLoadCallback.onBitmapLoaded(result.mBitmapResult, result.mExifInfo, PictureMimeType.isContent(inputUriString) ? inputUriString : mInputUri.getPath(), (mOutputUri == null) ? null : mOutputUri.getPath());
} else {
mBitmapLoadCallback.onFailure(result.mBitmapWorkerException);
}
}
private boolean checkSize(Bitmap bitmap, BitmapFactory.Options options) {
int bitmapSize = bitmap != null ? bitmap.getByteCount() : 0;
if (bitmapSize > MAX_BITMAP_SIZE) {
options.inSampleSize *= 2;
return true;
}
return false;
}
}