• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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