Files
message_q/src/com/android/messaging/ui/mediapicker/ImagePersistTask.java
T
Michael W 3b7f05ba67 Messaging: Replace ExifInterface with its androidx counterpart
Change-Id: I35ba14886b9f837d576f1cd385869614454c921f
2024-12-26 14:55:12 +01:00

174 lines
6.9 KiB
Java

/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui.mediapicker;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.net.Uri;
import com.android.messaging.datamodel.MediaScratchFileProvider;
import com.android.messaging.util.Assert;
import com.android.messaging.util.ContentType;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.SafeAsyncTask;
import com.android.messaging.util.exif.ExifInterface;
import com.android.messaging.util.exif.ExifTag;
import java.io.IOException;
import java.io.OutputStream;
public class ImagePersistTask extends SafeAsyncTask<Void, Void, Void> {
private static final String JPEG_EXTENSION = "jpg";
private static final String TAG = LogUtil.BUGLE_TAG;
private int mWidth;
private int mHeight;
private final float mHeightPercent;
private final byte[] mBytes;
private final Context mContext;
private final CameraManager.MediaCallback mCallback;
private Uri mOutputUri;
private Exception mException;
public ImagePersistTask(
final int width,
final int height,
final float heightPercent,
final byte[] bytes,
final Context context,
final CameraManager.MediaCallback callback) {
Assert.isTrue(heightPercent >= 0 && heightPercent <= 1);
Assert.notNull(bytes);
Assert.notNull(context);
Assert.notNull(callback);
mWidth = width;
mHeight = height;
mHeightPercent = heightPercent;
mBytes = bytes;
mContext = context;
mCallback = callback;
// TODO: We probably want to store directly in MMS storage to prevent this
// intermediate step
mOutputUri = MediaScratchFileProvider.buildMediaScratchSpaceUri(JPEG_EXTENSION);
}
@Override
protected Void doInBackgroundTimed(final Void... params) {
OutputStream outputStream = null;
Bitmap bitmap = null;
Bitmap clippedBitmap = null;
try {
outputStream =
mContext.getContentResolver().openOutputStream(mOutputUri);
if (mHeightPercent != 1.0f) {
int orientation = androidx.exifinterface.media.ExifInterface.ORIENTATION_UNDEFINED;
final ExifInterface exifInterface = new ExifInterface();
try {
exifInterface.readExif(mBytes);
final Integer orientationValue =
exifInterface.getTagIntValue(ExifInterface.TAG_ORIENTATION);
if (orientationValue != null) {
orientation = orientationValue.intValue();
}
// The thumbnail is of the full image, but we're cropping it, so just clear
// the thumbnail
exifInterface.setCompressedThumbnail((byte[]) null);
} catch (IOException e) {
// Couldn't get exif tags, not the end of the world
}
bitmap = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
final int clippedWidth;
final int clippedHeight;
if (ExifInterface.getOrientationParams(orientation).invertDimensions) {
Assert.equals(mWidth, bitmap.getHeight());
Assert.equals(mHeight, bitmap.getWidth());
clippedWidth = (int) (mHeight * mHeightPercent);
clippedHeight = mWidth;
} else {
Assert.equals(mWidth, bitmap.getWidth());
Assert.equals(mHeight, bitmap.getHeight());
clippedWidth = mWidth;
clippedHeight = (int) (mHeight * mHeightPercent);
}
final int offsetTop = (bitmap.getHeight() - clippedHeight) / 2;
final int offsetLeft = (bitmap.getWidth() - clippedWidth) / 2;
mWidth = clippedWidth;
mHeight = clippedHeight;
clippedBitmap = Bitmap.createBitmap(clippedWidth, clippedHeight,
Bitmap.Config.ARGB_8888);
clippedBitmap.setDensity(bitmap.getDensity());
final Canvas clippedBitmapCanvas = new Canvas(clippedBitmap);
final Matrix matrix = new Matrix();
matrix.postTranslate(-offsetLeft, -offsetTop);
clippedBitmapCanvas.drawBitmap(bitmap, matrix, null /* paint */);
clippedBitmapCanvas.save();
// EXIF data can take a big chunk of the file size and is often cleared by the
// carrier, only store orientation since that's critical
ExifTag orientationTag = exifInterface.getTag(ExifInterface.TAG_ORIENTATION);
exifInterface.clearExif();
exifInterface.setTag(orientationTag);
exifInterface.writeExif(clippedBitmap, outputStream);
} else {
outputStream.write(mBytes);
}
} catch (final IOException e) {
mOutputUri = null;
mException = e;
LogUtil.e(TAG, "Unable to persist image to temp storage " + e);
} finally {
if (bitmap != null) {
bitmap.recycle();
}
if (clippedBitmap != null) {
clippedBitmap.recycle();
}
if (outputStream != null) {
try {
outputStream.flush();
} catch (final IOException e) {
mOutputUri = null;
mException = e;
LogUtil.e(TAG, "error trying to flush and close the outputStream" + e);
} finally {
try {
outputStream.close();
} catch (final IOException e) {
// Do nothing.
}
}
}
}
return null;
}
@Override
protected void onPostExecute(final Void aVoid) {
if (mOutputUri != null) {
mCallback.onMediaReady(mOutputUri, ContentType.IMAGE_JPEG, mWidth, mHeight);
} else {
Assert.notNull(mException);
mCallback.onMediaFailed(mException);
}
}
}