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