• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.messaging.ui.mediapicker;
18 
19 import android.content.Context;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.graphics.Canvas;
23 import android.graphics.Matrix;
24 import android.net.Uri;
25 
26 import com.android.messaging.datamodel.MediaScratchFileProvider;
27 import com.android.messaging.util.Assert;
28 import com.android.messaging.util.ContentType;
29 import com.android.messaging.util.LogUtil;
30 import com.android.messaging.util.SafeAsyncTask;
31 import com.android.messaging.util.exif.ExifInterface;
32 import com.android.messaging.util.exif.ExifTag;
33 
34 import java.io.IOException;
35 import java.io.OutputStream;
36 
37 public class ImagePersistTask extends SafeAsyncTask<Void, Void, Void> {
38     private static final String JPEG_EXTENSION = "jpg";
39     private static final String TAG = LogUtil.BUGLE_TAG;
40 
41     private int mWidth;
42     private int mHeight;
43     private final float mHeightPercent;
44     private final byte[] mBytes;
45     private final Context mContext;
46     private final CameraManager.MediaCallback mCallback;
47     private Uri mOutputUri;
48     private Exception mException;
49 
ImagePersistTask( final int width, final int height, final float heightPercent, final byte[] bytes, final Context context, final CameraManager.MediaCallback callback)50     public ImagePersistTask(
51             final int width,
52             final int height,
53             final float heightPercent,
54             final byte[] bytes,
55             final Context context,
56             final CameraManager.MediaCallback callback) {
57         Assert.isTrue(heightPercent >= 0 && heightPercent <= 1);
58         Assert.notNull(bytes);
59         Assert.notNull(context);
60         Assert.notNull(callback);
61         mWidth = width;
62         mHeight = height;
63         mHeightPercent = heightPercent;
64         mBytes = bytes;
65         mContext = context;
66         mCallback = callback;
67         // TODO: We probably want to store directly in MMS storage to prevent this
68         // intermediate step
69         mOutputUri = MediaScratchFileProvider.buildMediaScratchSpaceUri(JPEG_EXTENSION);
70     }
71 
72     @Override
doInBackgroundTimed(final Void... params)73     protected Void doInBackgroundTimed(final Void... params) {
74         OutputStream outputStream = null;
75         Bitmap bitmap = null;
76         Bitmap clippedBitmap = null;
77         try {
78             outputStream =
79                     mContext.getContentResolver().openOutputStream(mOutputUri);
80             if (mHeightPercent != 1.0f) {
81                 int orientation = android.media.ExifInterface.ORIENTATION_UNDEFINED;
82                 final ExifInterface exifInterface = new ExifInterface();
83                 try {
84                     exifInterface.readExif(mBytes);
85                     final Integer orientationValue =
86                             exifInterface.getTagIntValue(ExifInterface.TAG_ORIENTATION);
87                     if (orientationValue != null) {
88                         orientation = orientationValue.intValue();
89                     }
90                     // The thumbnail is of the full image, but we're cropping it, so just clear
91                     // the thumbnail
92                     exifInterface.setCompressedThumbnail((byte[]) null);
93                 } catch (IOException e) {
94                     // Couldn't get exif tags, not the end of the world
95                 }
96                 bitmap = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
97                 final int clippedWidth;
98                 final int clippedHeight;
99                 if (ExifInterface.getOrientationParams(orientation).invertDimensions) {
100                     Assert.equals(mWidth, bitmap.getHeight());
101                     Assert.equals(mHeight, bitmap.getWidth());
102                     clippedWidth = (int) (mHeight * mHeightPercent);
103                     clippedHeight = mWidth;
104                 } else {
105                     Assert.equals(mWidth, bitmap.getWidth());
106                     Assert.equals(mHeight, bitmap.getHeight());
107                     clippedWidth = mWidth;
108                     clippedHeight = (int) (mHeight * mHeightPercent);
109                 }
110                 final int offsetTop = (bitmap.getHeight() - clippedHeight) / 2;
111                 final int offsetLeft = (bitmap.getWidth() - clippedWidth) / 2;
112                 mWidth = clippedWidth;
113                 mHeight = clippedHeight;
114                 clippedBitmap = Bitmap.createBitmap(clippedWidth, clippedHeight,
115                         Bitmap.Config.ARGB_8888);
116                 clippedBitmap.setDensity(bitmap.getDensity());
117                 final Canvas clippedBitmapCanvas = new Canvas(clippedBitmap);
118                 final Matrix matrix = new Matrix();
119                 matrix.postTranslate(-offsetLeft, -offsetTop);
120                 clippedBitmapCanvas.drawBitmap(bitmap, matrix, null /* paint */);
121                 clippedBitmapCanvas.save();
122                 // EXIF data can take a big chunk of the file size and is often cleared by the
123                 // carrier, only store orientation since that's critical
124                 ExifTag orientationTag = exifInterface.getTag(ExifInterface.TAG_ORIENTATION);
125                 exifInterface.clearExif();
126                 exifInterface.setTag(orientationTag);
127                 exifInterface.writeExif(clippedBitmap, outputStream);
128             } else {
129                 outputStream.write(mBytes);
130             }
131         } catch (final IOException e) {
132             mOutputUri = null;
133             mException = e;
134             LogUtil.e(TAG, "Unable to persist image to temp storage " + e);
135         } finally {
136             if (bitmap != null) {
137                 bitmap.recycle();
138             }
139 
140             if (clippedBitmap != null) {
141                 clippedBitmap.recycle();
142             }
143 
144             if (outputStream != null) {
145                 try {
146                     outputStream.flush();
147                 } catch (final IOException e) {
148                     mOutputUri = null;
149                     mException = e;
150                     LogUtil.e(TAG, "error trying to flush and close the outputStream" + e);
151                 } finally {
152                     try {
153                         outputStream.close();
154                     } catch (final IOException e) {
155                         // Do nothing.
156                     }
157                 }
158             }
159         }
160         return null;
161     }
162 
163     @Override
onPostExecute(final Void aVoid)164     protected void onPostExecute(final Void aVoid) {
165         if (mOutputUri != null) {
166             mCallback.onMediaReady(mOutputUri, ContentType.IMAGE_JPEG, mWidth, mHeight);
167         } else {
168             Assert.notNull(mException);
169             mCallback.onMediaFailed(mException);
170         }
171     }
172 }
173