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.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.BitmapFactory; 22 import android.net.Uri; 23 import android.support.annotation.NonNull; 24 import android.support.v4.content.FileProvider; 25 import com.android.dialer.callcomposer.camera.ImagePersistWorker.Result; 26 import com.android.dialer.callcomposer.camera.exif.ExifInterface; 27 import com.android.dialer.callcomposer.util.BitmapResizer; 28 import com.android.dialer.common.Assert; 29 import com.android.dialer.common.concurrent.DialerExecutor.Worker; 30 import com.android.dialer.constants.Constants; 31 import com.android.dialer.util.DialerUtils; 32 import com.google.auto.value.AutoValue; 33 import java.io.File; 34 import java.io.FileOutputStream; 35 import java.io.IOException; 36 import java.io.OutputStream; 37 38 /** Persisting image routine. */ 39 public class ImagePersistWorker implements Worker<Void, Result> { 40 private int width; 41 private int height; 42 private final float heightPercent; 43 private final byte[] bytes; 44 private final Context context; 45 46 @AutoValue 47 abstract static class Result { 48 builder()49 public static Builder builder() { 50 return new AutoValue_ImagePersistWorker_Result.Builder(); 51 } 52 53 @NonNull getUri()54 abstract Uri getUri(); 55 getWidth()56 abstract int getWidth(); 57 getHeight()58 abstract int getHeight(); 59 60 @AutoValue.Builder 61 abstract static class Builder { setUri(@onNull Uri uri)62 abstract Builder setUri(@NonNull Uri uri); 63 setWidth(int width)64 abstract Builder setWidth(int width); 65 setHeight(int height)66 abstract Builder setHeight(int height); 67 build()68 abstract Result build(); 69 } 70 } 71 ImagePersistWorker( final int width, final int height, final float heightPercent, final byte[] bytes, final Context context)72 ImagePersistWorker( 73 final int width, 74 final int height, 75 final float heightPercent, 76 final byte[] bytes, 77 final Context context) { 78 Assert.checkArgument(heightPercent >= 0 && heightPercent <= 1); 79 Assert.isNotNull(bytes); 80 Assert.isNotNull(context); 81 this.width = width; 82 this.height = height; 83 this.heightPercent = heightPercent; 84 this.bytes = bytes; 85 this.context = context; 86 } 87 88 @Override doInBackground(Void unused)89 public Result doInBackground(Void unused) throws Exception { 90 File outputFile = DialerUtils.createShareableFile(context); 91 92 try (OutputStream outputStream = new FileOutputStream(outputFile)) { 93 writeClippedBitmap(outputStream); 94 } 95 96 return Result.builder() 97 .setUri( 98 FileProvider.getUriForFile( 99 context, Constants.get().getFileProviderAuthority(), outputFile)) 100 .setWidth(width) 101 .setHeight(height) 102 .build(); 103 } 104 writeClippedBitmap(OutputStream outputStream)105 private void writeClippedBitmap(OutputStream outputStream) throws IOException { 106 int orientation = android.media.ExifInterface.ORIENTATION_UNDEFINED; 107 final ExifInterface exifInterface = new ExifInterface(); 108 try { 109 exifInterface.readExif(bytes); 110 final Integer orientationValue = exifInterface.getTagIntValue(ExifInterface.TAG_ORIENTATION); 111 if (orientationValue != null) { 112 orientation = orientationValue.intValue(); 113 } 114 } catch (final IOException e) { 115 // Couldn't get exif tags, not the end of the world 116 } 117 118 ExifInterface.OrientationParams params = ExifInterface.getOrientationParams(orientation); 119 Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); 120 final int clippedWidth; 121 final int clippedHeight; 122 if (params.invertDimensions) { 123 Assert.checkState(width == bitmap.getHeight()); 124 Assert.checkState(height == bitmap.getWidth()); 125 clippedWidth = (int) (height * heightPercent); 126 clippedHeight = width; 127 } else { 128 Assert.checkState(width == bitmap.getWidth()); 129 Assert.checkState(height == bitmap.getHeight()); 130 clippedWidth = width; 131 clippedHeight = (int) (height * heightPercent); 132 } 133 134 int offsetTop = (bitmap.getHeight() - clippedHeight) / 2; 135 int offsetLeft = (bitmap.getWidth() - clippedWidth) / 2; 136 width = clippedWidth; 137 height = clippedHeight; 138 139 Bitmap clippedBitmap = 140 Bitmap.createBitmap(bitmap, offsetLeft, offsetTop, clippedWidth, clippedHeight); 141 clippedBitmap = BitmapResizer.resizeForEnrichedCalling(clippedBitmap, params.rotation); 142 // EXIF data can take a big chunk of the file size and we've already manually rotated our image, 143 // so remove all of the exif data. 144 exifInterface.clearExif(); 145 exifInterface.writeExif(clippedBitmap, outputStream); 146 147 clippedBitmap.recycle(); 148 bitmap.recycle(); 149 } 150 } 151