1 /* 2 * Copyright (C) 2014 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.camera.one.v2.imagesaver; 18 19 import com.android.camera.one.v2.camera2proxy.ImageProxy; 20 import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy; 21 import com.google.common.base.Optional; 22 import com.google.common.util.concurrent.ListenableFuture; 23 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.List; 27 import java.util.Map; 28 29 import javax.annotation.Nullable; 30 import javax.annotation.ParametersAreNonnullByDefault; 31 32 /** 33 * Saves the last image in a burst. 34 */ 35 @ParametersAreNonnullByDefault 36 public class MostRecentImageSaver implements ImageSaver { 37 private final SingleImageSaver mSingleImageSaver; 38 private final Map<Long, ImageProxy> mThumbnails; 39 private final Map<Long, MetadataImage> mFullSizeImages; 40 41 public MostRecentImageSaver(SingleImageSaver singleImageSaver) { 42 mSingleImageSaver = singleImageSaver; 43 mThumbnails = new HashMap<>(); 44 mFullSizeImages = new HashMap<>(); 45 } 46 47 @Override 48 public void addThumbnail(ImageProxy imageProxy) { 49 mThumbnails.put(imageProxy.getTimestamp(), imageProxy); 50 closeOlderImages(); 51 } 52 53 @Override 54 public void addFullSizeImage(ImageProxy imageProxy, 55 ListenableFuture<TotalCaptureResultProxy> metadata) { 56 mFullSizeImages.put(imageProxy.getTimestamp(), new MetadataImage(imageProxy, metadata)); 57 closeOlderImages(); 58 } 59 60 @Override 61 public void close() { 62 try { 63 MetadataImage fullSize = getLastImage(); 64 if (fullSize != null) { 65 // Pop the image out of the map so that closeAllImages() does 66 // not close it. 67 mFullSizeImages.remove(fullSize.getTimestamp()); 68 } else { 69 return; 70 } 71 72 ImageProxy thumbnail = getThumbnail(fullSize.getTimestamp()); 73 if (thumbnail != null) { 74 // Pop the image out of the map so that closeAllImages() does 75 // not close it. 76 mThumbnails.remove(thumbnail.getTimestamp()); 77 } 78 79 mSingleImageSaver.saveAndCloseImage(fullSize, Optional.fromNullable(thumbnail), 80 fullSize.getMetadata()); 81 } finally { 82 closeAllImages(); 83 } 84 } 85 86 private void closeAllImages() { 87 for (ImageProxy image : mThumbnails.values()) { 88 image.close(); 89 } 90 91 for (ImageProxy image : mFullSizeImages.values()) { 92 image.close(); 93 } 94 } 95 96 private void closeOlderImages(long threshold, Map<Long, ? extends ImageProxy> imageMap) { 97 List<Long> toRemove = new ArrayList<>(); 98 for (long imageTimestamp : imageMap.keySet()) { 99 if (imageTimestamp < threshold) { 100 imageMap.get(imageTimestamp).close(); 101 toRemove.add(imageTimestamp); 102 } 103 } 104 for (Long timestamp : toRemove) { 105 imageMap.remove(timestamp); 106 } 107 } 108 109 private void closeOlderImages() { 110 Optional<Long> timestampThreshold = getMostRecentFullSizeImageTimestamp(); 111 if (timestampThreshold.isPresent()) { 112 closeOlderImages(timestampThreshold.get(), mFullSizeImages); 113 closeOlderImages(timestampThreshold.get(), mThumbnails); 114 } 115 } 116 117 private Optional<Long> getMostRecentFullSizeImageTimestamp() { 118 if (mFullSizeImages.isEmpty()) { 119 return Optional.absent(); 120 } 121 boolean pairFound = false; 122 long oldestTimestamp = 0; 123 for (ImageProxy image : mFullSizeImages.values()) { 124 long timestamp = image.getTimestamp(); 125 if (!pairFound || timestamp > oldestTimestamp) { 126 oldestTimestamp = timestamp; 127 pairFound = true; 128 } 129 } 130 if (!pairFound) { 131 return Optional.absent(); 132 } else { 133 return Optional.of(oldestTimestamp); 134 } 135 } 136 137 @Nullable 138 private MetadataImage getLastImage() { 139 if (mFullSizeImages.isEmpty()) { 140 return null; 141 } 142 MetadataImage lastImage = null; 143 for (MetadataImage image : mFullSizeImages.values()) { 144 if (lastImage == null || image.getTimestamp() > lastImage.getTimestamp()) { 145 lastImage = image; 146 } 147 } 148 return lastImage; 149 } 150 151 @Nullable 152 private ImageProxy getThumbnail(long timestamp) { 153 return mThumbnails.get(timestamp); 154 } 155 } 156