14dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin/* 24dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * Copyright (C) 2015 The Android Open Source Project 34dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * 44dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * Licensed under the Apache License, Version 2.0 (the "License"); 54dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * you may not use this file except in compliance with the License. 64dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * You may obtain a copy of the License at 74dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * 84dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * http://www.apache.org/licenses/LICENSE-2.0 94dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * 104dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * Unless required by applicable law or agreed to in writing, software 114dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * distributed under the License is distributed on an "AS IS" BASIS, 124dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * See the License for the specific language governing permissions and 144dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * limitations under the License. 154dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin */ 164dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 174dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linpackage com.android.camera.one.v2.imagesaver; 184dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 194dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport android.graphics.Bitmap; 204dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport android.graphics.BitmapFactory; 212f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport android.graphics.Rect; 224dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport android.net.Uri; 234dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 24f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Linimport com.android.camera.Exif; 254dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.app.OrientationManager; 264dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.debug.Log; 274dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.one.OneCamera; 284dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.one.v2.camera2proxy.ImageProxy; 29d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lallimport com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy; 304dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.one.v2.photo.ImageRotationCalculator; 314dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.processing.imagebackend.ImageBackend; 324dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.processing.imagebackend.ImageConsumer; 334dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.processing.imagebackend.ImageProcessorListener; 344dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.processing.imagebackend.ImageProcessorProxyListener; 354dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.processing.imagebackend.ImageToProcess; 364dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.processing.imagebackend.TaskImageContainer; 374dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.session.CaptureSession; 380f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin 390f0329889f69182648fe8f535335e48978d63cc0I-Jong Linimport com.google.common.annotations.VisibleForTesting; 404dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.google.common.base.Optional; 41d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lallimport com.google.common.util.concurrent.ListenableFuture; 424dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 434dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport java.util.HashSet; 444dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport java.util.Set; 450f0329889f69182648fe8f535335e48978d63cc0I-Jong Linimport java.util.concurrent.Executor; 460f0329889f69182648fe8f535335e48978d63cc0I-Jong Linimport java.util.concurrent.Executors; 474dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 484dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport javax.annotation.Nonnull; 494dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport javax.annotation.ParametersAreNonnullByDefault; 504dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 514dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin/** 524dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * Wires up the ImageBackend task submission process to save JPEG images. Camera 534dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * delivers a JPEG-compressed full-size image. This class does very little work 544dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * and just routes this image artifact as the thumbnail and to remote devices. 554dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin */ 564dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linpublic class JpegImageBackendImageSaver implements ImageSaver.Builder { 570f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin 584dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @ParametersAreNonnullByDefault 590f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin private final class ImageSaverImpl implements SingleImageSaver { 604dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final CaptureSession mSession; 614dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final OrientationManager.DeviceOrientation mImageRotation; 624dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final ImageBackend mImageBackend; 630f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin private final ImageProcessorListener mImageProcessorListener; 644dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 650f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin public ImageSaverImpl(CaptureSession session, 660f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin OrientationManager.DeviceOrientation imageRotation, 670f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin ImageBackend imageBackend, ImageProcessorListener imageProcessorListener) { 684dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mSession = session; 694dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mImageRotation = imageRotation; 704dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mImageBackend = imageBackend; 710f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin mImageProcessorListener = imageProcessorListener; 724dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 734dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 744dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Override 75d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall public void saveAndCloseImage(ImageProxy image, Optional<ImageProxy> thumbnail, 76d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall ListenableFuture<TotalCaptureResultProxy> metadata) { 774dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin // TODO: Use thumbnail to speed up RGB thumbnail creation whenever 784dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin // possible. For now, just close it. 794dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin if (thumbnail.isPresent()) { 804dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin thumbnail.get().close(); 814dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 824dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 834dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin Set<ImageConsumer.ImageTaskFlags> taskFlagsSet = new HashSet<>(); 844dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin taskFlagsSet.add(ImageConsumer.ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK); 854dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE); 864dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 874dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin try { 880f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin mImageBackend.receiveImage(new ImageToProcess(image, mImageRotation, metadata, 89c36bcac14302b1bb3d7a316f221685d4d6ad95cbI-Jong Lin mCrop), mExecutor, taskFlagsSet, mSession, 90c36bcac14302b1bb3d7a316f221685d4d6ad95cbI-Jong Lin Optional.of(mImageProcessorListener)); 914dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } catch (InterruptedException e) { 924dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin // Impossible exception because receiveImage is nonblocking 934dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin throw new RuntimeException(e); 944dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 954dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 964dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 974dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 980f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin private static class JpegImageProcessorListener implements ImageProcessorListener { 994dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final ImageProcessorProxyListener mListenerProxy; 1004dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final CaptureSession mSession; 1014dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final OrientationManager.DeviceOrientation mImageRotation; 1024dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final OneCamera.PictureSaverCallback mPictureSaverCallback; 1034dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1040f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin private JpegImageProcessorListener(ImageProcessorProxyListener listenerProxy, 1050f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin CaptureSession session, 1064dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin OrientationManager.DeviceOrientation imageRotation, 1074dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin OneCamera.PictureSaverCallback pictureSaverCallback) { 1084dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mListenerProxy = listenerProxy; 1094dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mSession = session; 1104dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mImageRotation = imageRotation; 1114dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mPictureSaverCallback = pictureSaverCallback; 1124dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 1134dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1144dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Override 1154dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin public void onStart(TaskImageContainer.TaskInfo task) { 1164dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 1174dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1184dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Override 1194dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin public void onResultCompressed(TaskImageContainer.TaskInfo task, 1204dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin TaskImageContainer.CompressedPayload payload) { 1214dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin if (task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) { 1224dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin // Just start the thumbnail now, since there's no earlier event. 1237a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin 124d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall // Downsample and convert the JPEG payload to a reasonably-sized 125d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall // Bitmap 1267a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin BitmapFactory.Options options = new BitmapFactory.Options(); 1277a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin options.inSampleSize = JPEG_DOWNSAMPLE_FOR_FAST_INDICATOR; 1284dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin final Bitmap bitmap = BitmapFactory.decodeByteArray(payload.data, 0, 1297a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin payload.data.length, options); 1307a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin 131f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // If the rotation is implemented as an EXIF flag, we need to 132f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // pass this information onto the UI call, since the rotation is 133f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // NOT applied to the bitmap directly. 134f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin int rotation = Exif.getOrientation(payload.data); 1356dd5840e86bffecba8a7f52327b9c1085caeb7d2Sascha Haeberling mSession.updateCaptureIndicatorThumbnail(bitmap, rotation); 1364dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin // Send image to remote devices 1374dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mPictureSaverCallback.onRemoteThumbnailAvailable(payload.data); 1384dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 1394dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1404dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 1414dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1424dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Override 1434dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin public void onResultUncompressed(TaskImageContainer.TaskInfo task, 1444dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin TaskImageContainer.UncompressedPayload payload) { 1454dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin // Do Nothing 1464dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 1474dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1484dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Override 1494dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin public void onResultUri(TaskImageContainer.TaskInfo task, Uri uri) { 1500f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin // Do Nothing 1514dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 1524dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 1530f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin 154d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall /** Factor to downsample full-size JPEG image for use in thumbnail bitmap. */ 155d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall private static final int JPEG_DOWNSAMPLE_FOR_FAST_INDICATOR = 4; 156d0316626dd2c2f4bd37ac7a198dbfe9e0ce2277fPuneet Lall private static Log.Tag TAG = new Log.Tag("JpegImgBESaver"); 1574dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final ImageRotationCalculator mImageRotationCalculator; 1584dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin private final ImageBackend mImageBackend; 1590f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin private final Executor mExecutor; 160c36bcac14302b1bb3d7a316f221685d4d6ad95cbI-Jong Lin private final Rect mCrop; 161c36bcac14302b1bb3d7a316f221685d4d6ad95cbI-Jong Lin 1624dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1634dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin /** 1640f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * Constructor Instantiate a local instance executor for all JPEG ImageSaver 1650f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * factory requests via constructor. 1660f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * 1674dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * @param imageRotationCalculator the image rotation calculator to determine 1680f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * @param imageBackend ImageBackend to run the image tasks 1694dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin */ 1700f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin public JpegImageBackendImageSaver( 1710f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin ImageRotationCalculator imageRotationCalculator, 172c36bcac14302b1bb3d7a316f221685d4d6ad95cbI-Jong Lin ImageBackend imageBackend, Rect crop) { 1734dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mImageRotationCalculator = imageRotationCalculator; 1744dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin mImageBackend = imageBackend; 1750f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin mExecutor = Executors.newSingleThreadExecutor(); 176c36bcac14302b1bb3d7a316f221685d4d6ad95cbI-Jong Lin mCrop = crop; 1770f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin } 1780f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin 1790f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin /** 1800f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * Constructor for dependency injection/ testing. 1810f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * 1820f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * @param imageRotationCalculator the image rotation calculator to determine 1830f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * @param imageBackend ImageBackend to run the image tasks 1840f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * @param executor Executor to be used for listener events in ImageBackend. 1850f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin */ 1860f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin @VisibleForTesting 1870f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin public JpegImageBackendImageSaver( 1880f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin ImageRotationCalculator imageRotationCalculator, 189c36bcac14302b1bb3d7a316f221685d4d6ad95cbI-Jong Lin ImageBackend imageBackend, Executor executor, Rect crop) { 1900f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin mImageRotationCalculator = imageRotationCalculator; 1910f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin mImageBackend = imageBackend; 1920f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin mExecutor = executor; 193c36bcac14302b1bb3d7a316f221685d4d6ad95cbI-Jong Lin mCrop = crop; 1944dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 1954dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1964dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin /** 1974dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * Builder for the Zsl/ImageBackend Interface 1984dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * 1994dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * @return Instantiated interface object 2004dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin */ 2014dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Override 2024dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin public ImageSaver build( 2034dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Nonnull OneCamera.PictureSaverCallback pictureSaverCallback, 2044dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Nonnull OrientationManager.DeviceOrientation orientation, 2054dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin @Nonnull CaptureSession session) { 2064dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin final OrientationManager.DeviceOrientation imageRotation = mImageRotationCalculator 207f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin .toImageRotation(); 2084dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 2094dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin ImageProcessorProxyListener proxyListener = mImageBackend.getProxyListener(); 2104dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 2110f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin JpegImageProcessorListener jpegImageProcessorListener = new JpegImageProcessorListener( 2124dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin proxyListener, session, imageRotation, pictureSaverCallback); 2130f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin return new MostRecentImageSaver(new ImageSaverImpl(session, 2140f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin imageRotation, mImageBackend, jpegImageProcessorListener)); 2154dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 2164dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin} 217