Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2016 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.dialer.callcomposer.camera;
     18 
     19 import android.annotation.TargetApi;
     20 import android.content.Context;
     21 import android.graphics.Bitmap;
     22 import android.graphics.BitmapFactory;
     23 import android.graphics.Canvas;
     24 import android.graphics.Matrix;
     25 import android.net.Uri;
     26 import android.os.Build.VERSION_CODES;
     27 import android.support.v4.content.FileProvider;
     28 import com.android.dialer.callcomposer.camera.exif.ExifInterface;
     29 import com.android.dialer.callcomposer.camera.exif.ExifTag;
     30 import com.android.dialer.callcomposer.util.BitmapResizer;
     31 import com.android.dialer.common.Assert;
     32 import com.android.dialer.common.concurrent.FallibleAsyncTask;
     33 import com.android.dialer.constants.Constants;
     34 import com.android.dialer.util.DialerUtils;
     35 import java.io.File;
     36 import java.io.FileOutputStream;
     37 import java.io.IOException;
     38 import java.io.OutputStream;
     39 
     40 /** Persisting image routine. */
     41 @TargetApi(VERSION_CODES.M)
     42 public class ImagePersistTask extends FallibleAsyncTask<Void, Void, Uri> {
     43   private int mWidth;
     44   private int mHeight;
     45   private final float mHeightPercent;
     46   private final byte[] mBytes;
     47   private final Context mContext;
     48   private final CameraManager.MediaCallback mCallback;
     49 
     50   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.checkArgument(heightPercent >= 0 && heightPercent <= 1);
     58     Assert.isNotNull(bytes);
     59     Assert.isNotNull(context);
     60     Assert.isNotNull(callback);
     61     mWidth = width;
     62     mHeight = height;
     63     mHeightPercent = heightPercent;
     64     mBytes = bytes;
     65     mContext = context;
     66     mCallback = callback;
     67   }
     68 
     69   @Override
     70   protected Uri doInBackgroundFallible(final Void... params) throws Exception {
     71     File outputFile = DialerUtils.createShareableFile(mContext);
     72 
     73     try (OutputStream outputStream = new FileOutputStream(outputFile)) {
     74       if (mHeightPercent != 1.0f) {
     75         writeClippedBitmap(outputStream);
     76       } else {
     77         Bitmap bitmap = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
     78         bitmap = BitmapResizer.resizeForEnrichedCalling(bitmap);
     79         bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
     80       }
     81     }
     82 
     83     return FileProvider.getUriForFile(
     84         mContext, Constants.get().getFileProviderAuthority(), outputFile);
     85   }
     86 
     87   @Override
     88   protected void onPostExecute(FallibleTaskResult<Uri> result) {
     89     if (result.isFailure()) {
     90       mCallback.onMediaFailed(new Exception("Persisting image failed", result.getThrowable()));
     91     } else {
     92       mCallback.onMediaReady(result.getResult(), "image/jpeg", mWidth, mHeight);
     93     }
     94   }
     95 
     96   private void writeClippedBitmap(OutputStream outputStream) throws IOException {
     97     int orientation = android.media.ExifInterface.ORIENTATION_UNDEFINED;
     98     final ExifInterface exifInterface = new ExifInterface();
     99     try {
    100       exifInterface.readExif(mBytes);
    101       final Integer orientationValue = exifInterface.getTagIntValue(ExifInterface.TAG_ORIENTATION);
    102       if (orientationValue != null) {
    103         orientation = orientationValue.intValue();
    104       }
    105     } catch (final IOException e) {
    106       // Couldn't get exif tags, not the end of the world
    107     }
    108     Bitmap bitmap = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
    109     final int clippedWidth;
    110     final int clippedHeight;
    111     if (ExifInterface.getOrientationParams(orientation).invertDimensions) {
    112       Assert.checkState(mWidth == bitmap.getHeight());
    113       Assert.checkState(mHeight == bitmap.getWidth());
    114       clippedWidth = (int) (mHeight * mHeightPercent);
    115       clippedHeight = mWidth;
    116     } else {
    117       Assert.checkState(mWidth == bitmap.getWidth());
    118       Assert.checkState(mHeight == bitmap.getHeight());
    119       clippedWidth = mWidth;
    120       clippedHeight = (int) (mHeight * mHeightPercent);
    121     }
    122     final int offsetTop = (bitmap.getHeight() - clippedHeight) / 2;
    123     final int offsetLeft = (bitmap.getWidth() - clippedWidth) / 2;
    124     mWidth = clippedWidth;
    125     mHeight = clippedHeight;
    126     Bitmap clippedBitmap =
    127         Bitmap.createBitmap(clippedWidth, clippedHeight, Bitmap.Config.ARGB_8888);
    128     clippedBitmap.setDensity(bitmap.getDensity());
    129     final Canvas clippedBitmapCanvas = new Canvas(clippedBitmap);
    130     final Matrix matrix = new Matrix();
    131     matrix.postTranslate(-offsetLeft, -offsetTop);
    132     clippedBitmapCanvas.drawBitmap(bitmap, matrix, null /* paint */);
    133     clippedBitmapCanvas.save();
    134     clippedBitmap = BitmapResizer.resizeForEnrichedCalling(clippedBitmap);
    135     // EXIF data can take a big chunk of the file size and is often cleared by the
    136     // carrier, only store orientation since that's critical
    137     final ExifTag orientationTag = exifInterface.getTag(ExifInterface.TAG_ORIENTATION);
    138     exifInterface.clearExif();
    139     exifInterface.setTag(orientationTag);
    140     exifInterface.writeExif(clippedBitmap, outputStream);
    141 
    142     clippedBitmap.recycle();
    143     bitmap.recycle();
    144   }
    145 }
    146