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