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 */ SequentialTimestampGenerator(long seedTimestampMillis)55 public SequentialTimestampGenerator(long seedTimestampMillis) { 56 mSeedTimestampMillis = seedTimestampMillis; 57 } 58 59 /** 60 * Returns the next timestamp. 61 */ getNextTimestampMillis()62 public synchronized long getNextTimestampMillis() { 63 mSeedTimestampMillis += TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS); 64 return mSeedTimestampMillis; 65 } 66 } 67 logArtifactCount(final Map<String, Integer> artifactTypeCount)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 */ saveBurstResultsInBackground(final BurstResult burstResult, final StackSaver stackSaver, final Runnable onCompletetionCallback)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 */ saveArtifacts(final StackSaver stackSaver, final BurstResult burstResult, final String artifactType, SequentialTimestampGenerator timestampGenerator)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 saveBurstMediaItem(StackSaver stackSaver, BurstMediaItem mediaItem, String artifactType, int artifactIndex, int index, SequentialTimestampGenerator timestampGenerator)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 logProgressUpdate(String[] artifactTypes, BurstResult burstResult)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