1 /* 2 * Copyright (C) 2015 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.burst; 18 19 import android.os.AsyncTask; 20 import android.text.TextUtils; 21 22 import com.android.camera.debug.Log; 23 import com.android.camera.debug.Log.Tag; 24 import com.android.camera.session.StackSaver; 25 26 import java.util.ArrayList; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.concurrent.TimeUnit; 30 31 class BurstResultsSaver { 32 private static final Tag TAG = new Tag("BurstResultsSaver"); 33 34 /** 35 * The format string of burst media item file name (without extension). 36 * <p/> 37 * An media item file name has the following format: "Burst_" + artifact 38 * type + "_" + index of artifact + "_" + index of media item + "_" + 39 * timestamp 40 */ 41 private static final String MEDIA_ITEM_FILENAME_FORMAT_STRING = "Burst_%s_%d_%d_%d"; 42 43 /** 44 * Generates sequential timestamp with 1 second difference. 45 */ 46 private static class SequentialTimestampGenerator { 47 private long mSeedTimestampMillis; 48 49 /** 50 * New instance of generator. 51 * 52 * @param seedTimestampMillis the timestamp in milliseconds for 53 * initializing the generator. 54 */ 55 public SequentialTimestampGenerator(long seedTimestampMillis) { 56 mSeedTimestampMillis = seedTimestampMillis; 57 } 58 59 /** 60 * Returns the next timestamp. 61 */ 62 public synchronized long getNextTimestampMillis() { 63 mSeedTimestampMillis += TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS); 64 return mSeedTimestampMillis; 65 } 66 } 67 68 public static void logArtifactCount(final Map<String, Integer> artifactTypeCount) { 69 final String prefix = "Finished burst. Creating "; 70 List<String> artifactDescription = new ArrayList<String>(); 71 for (Map.Entry<String, Integer> entry : artifactTypeCount.entrySet()) { 72 artifactDescription.add(entry.getValue() + " " + entry.getKey()); 73 } 74 75 String message = prefix + TextUtils.join(" and ", artifactDescription) + "."; 76 Log.d(TAG, message); 77 } 78 79 /** 80 * Saves the burst result and on completion re-enables the shutter button. 81 * 82 * @param burstResult the result of the burst. 83 */ 84 public static void saveBurstResultsInBackground(final BurstResult burstResult, 85 final StackSaver stackSaver, final Runnable onCompletetionCallback) { 86 Log.i(TAG, "Saving results of of the burst."); 87 88 AsyncTask<Void, String, Void> saveTask = 89 new AsyncTask<Void, String, Void>() { 90 @Override 91 protected Void doInBackground(Void... arg0) { 92 // The timestamp with which a media item is saved 93 // determines its place in the film strip. The newer 94 // items appear first. 95 // We save artifacts and their corresponding media 96 // items sequentially in the desired order. The order 97 // of the artifacts is implicitly defined by 98 // burstResult.getTypes() and the media items inside the 99 // artifacts are assumed to be sorted in ascending order 100 // by timestamps. 101 // We create a timestamp-generator that generates 102 // timestamps in order and use it to save timestamps. 103 SequentialTimestampGenerator timestampGen = 104 new SequentialTimestampGenerator(System.currentTimeMillis()); 105 for (String artifactType : burstResult.getTypes()) { 106 publishProgress(artifactType); 107 saveArtifacts(stackSaver, burstResult, artifactType, 108 timestampGen); 109 } 110 return null; 111 } 112 113 @Override 114 protected void onPostExecute(Void result) { 115 onCompletetionCallback.run(); 116 } 117 118 @Override 119 protected void onProgressUpdate(String... artifactTypes) { 120 logProgressUpdate(artifactTypes, burstResult); 121 } 122 }; 123 saveTask.execute(null, null, null); 124 } 125 126 /** 127 * Save individual artifacts for bursts. 128 */ 129 private static void saveArtifacts(final StackSaver stackSaver, final BurstResult burstResult, 130 final String artifactType, SequentialTimestampGenerator timestampGenerator) { 131 List<BurstArtifact> artifactList = burstResult.getArtifactsByType(artifactType); 132 for (int artifactIndex = 0; artifactIndex < artifactList.size(); artifactIndex++) { 133 List<BurstMediaItem> mediaItems = artifactList.get(artifactIndex).getMediaItems(); 134 for (int index = 0; index < mediaItems.size(); index++) { 135 saveBurstMediaItem(stackSaver, mediaItems.get(index), 136 artifactType, artifactIndex + 1, index + 1, timestampGenerator); 137 } 138 } 139 } 140 141 private static void saveBurstMediaItem(StackSaver stackSaver, 142 BurstMediaItem mediaItem, 143 String artifactType, 144 int artifactIndex, 145 int index, 146 SequentialTimestampGenerator timestampGenerator) { 147 // Use ordered timestamp for saving the media item, this way media 148 // items appear to be in the correct order when user swipes to the 149 // film strip. 150 long timestamp = timestampGenerator.getNextTimestampMillis(); 151 final String title = String.format(MEDIA_ITEM_FILENAME_FORMAT_STRING, 152 artifactType, artifactIndex, index, mediaItem.getTimestamp()); 153 String mimeType = mediaItem.getMimeType(); 154 155 stackSaver.saveStackedImage(mediaItem.getFilePath(), 156 title, 157 mediaItem.getWidth(), 158 mediaItem.getHeight(), 159 0, // Artifacts returned from burst have upright orientation. 160 timestamp, 161 mimeType); 162 } 163 164 private static void logProgressUpdate(String[] artifactTypes, BurstResult burstResult) { 165 for (String artifactType : artifactTypes) { 166 List<BurstArtifact> artifacts = 167 burstResult.getArtifactsByType(artifactType); 168 if (!artifacts.isEmpty()) { 169 Log.d(TAG, "Saving " + artifacts.size() 170 + " " + artifactType + "s."); 171 } 172 } 173 } 174 175 } 176