CaptureSessionImpl.java revision 636bce5ed9771df424a5ca46586648a11018179b
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 17package com.android.camera.session; 18 19import android.graphics.Bitmap; 20import android.graphics.BitmapFactory; 21import android.location.Location; 22import android.net.Uri; 23import android.os.AsyncTask; 24 25import com.android.camera.Storage; 26import com.android.camera.app.MediaSaver; 27import com.android.camera.data.FilmstripItemData; 28import com.android.camera.debug.Log; 29import com.android.camera.exif.ExifInterface; 30import com.android.camera.util.FileUtil; 31import com.android.camera.util.Size; 32 33import java.io.File; 34import java.io.IOException; 35import java.util.HashSet; 36 37/** 38 * The default implementation of the CaptureSession interface. This is the 39 * implementation we use for normal Camera use. 40 */ 41public class CaptureSessionImpl implements CaptureSession { 42 private static final Log.Tag TAG = new Log.Tag("CaptureSessionImpl"); 43 44 /** The capture session manager responsible for this session. */ 45 private final CaptureSessionManagerImpl mSessionManager; 46 /** Used for adding/removing/updating placeholders for in-progress sessions. */ 47 private final PlaceholderManager mPlaceholderManager; 48 /** Used to store images on disk and to add them to the media store. */ 49 private final MediaSaver mMediaSaver; 50 /** The title of the item being processed. */ 51 private final String mTitle; 52 /** These listeners get informed about progress updates. */ 53 private final HashSet<ProgressListener> mProgressListeners = new HashSet<>(); 54 private final long mSessionStartMillis; 55 /** 56 * The file that can be used to write the final JPEG output temporarily, 57 * before it is copied to the final location. 58 */ 59 private final TemporarySessionFile mTempOutputFile; 60 /** Saver that is used to store a stack of images. */ 61 private final StackSaver mStackSaver; 62 /** A URI of the item being processed. */ 63 private Uri mUri; 64 /** The location this session was created at. Used for media store. */ 65 private Location mLocation; 66 /** The current progress of this session in percent. */ 67 private int mProgressPercent = 0; 68 /** A message ID for the current progress state. */ 69 private CharSequence mProgressMessage; 70 /** A place holder for this capture session. */ 71 private PlaceholderManager.Session mPlaceHolderSession; 72 private Uri mContentUri; 73 /** Whether this image was finished. */ 74 private volatile boolean mIsFinished; 75 76 /** 77 * Creates a new {@link CaptureSession}. 78 * 79 * @param title the title of this session. 80 * @param sessionStartMillis the timestamp of this capture session (since 81 * epoch). 82 * @param location the location of this session, used for media store. 83 * @param temporarySessionFile used to create a temporary session file if 84 * necessary. 85 * @param captureSessionManager the capture session manager responsible for 86 * this session. 87 * @param placeholderManager used to add/update/remove session placeholders. 88 * @param mediaSaver used to store images on disk and add them to the media 89 * store. 90 * @param stackSaver used to save stacks of images that belong to this 91 * session. 92 */ 93 /* package */CaptureSessionImpl(String title, 94 long sessionStartMillis, Location location, TemporarySessionFile temporarySessionFile, 95 CaptureSessionManagerImpl captureSessionManager, PlaceholderManager placeholderManager, 96 MediaSaver mediaSaver, StackSaver stackSaver) { 97 mTitle = title; 98 mSessionStartMillis = sessionStartMillis; 99 mLocation = location; 100 mTempOutputFile = temporarySessionFile; 101 mSessionManager = captureSessionManager; 102 mPlaceholderManager = placeholderManager; 103 mMediaSaver = mediaSaver; 104 mStackSaver = stackSaver; 105 mIsFinished = false; 106 } 107 108 @Override 109 public String getTitle() { 110 return mTitle; 111 } 112 113 @Override 114 public Location getLocation() { 115 return mLocation; 116 } 117 118 @Override 119 public void setLocation(Location location) { 120 mLocation = location; 121 } 122 123 @Override 124 public synchronized int getProgress() { 125 return mProgressPercent; 126 } 127 128 @Override 129 public synchronized void setProgress(int percent) { 130 mProgressPercent = percent; 131 mSessionManager.notifyTaskProgress(mUri, mProgressPercent); 132 for (ProgressListener listener : mProgressListeners) { 133 listener.onProgressChanged(percent); 134 } 135 } 136 137 @Override 138 public synchronized CharSequence getProgressMessage() { 139 return mProgressMessage; 140 } 141 142 @Override 143 public synchronized void setProgressMessage(CharSequence message) { 144 mProgressMessage = message; 145 mSessionManager.notifyTaskProgressText(mUri, message); 146 for (ProgressListener listener : mProgressListeners) { 147 listener.onStatusMessageChanged(message); 148 } 149 } 150 151 @Override 152 public void updateThumbnail(Bitmap bitmap) { 153 mPlaceholderManager.replacePlaceholder(mPlaceHolderSession, bitmap); 154 mSessionManager.notifySessionUpdated(mUri); 155 } 156 157 @Override 158 public void updateCaptureIndicatorThumbnail(Bitmap indicator, int rotationDegrees) { 159 onCaptureIndicatorUpdate(indicator, rotationDegrees); 160 } 161 162 @Override 163 public synchronized void startEmpty(Size pictureSize) { 164 if (mIsFinished) { 165 return; 166 } 167 168 mProgressMessage = ""; 169 mPlaceHolderSession = mPlaceholderManager.insertEmptyPlaceholder(mTitle, pictureSize, 170 mSessionStartMillis); 171 mUri = mPlaceHolderSession.outputUri; 172 mSessionManager.putSession(mUri, this); 173 mSessionManager.notifyTaskQueued(mUri); 174 } 175 176 @Override 177 public synchronized void startSession(Bitmap placeholder, CharSequence progressMessage) { 178 if (mIsFinished) { 179 return; 180 } 181 182 mProgressMessage = progressMessage; 183 mPlaceHolderSession = mPlaceholderManager.insertPlaceholder(mTitle, placeholder, 184 mSessionStartMillis); 185 mUri = mPlaceHolderSession.outputUri; 186 mSessionManager.putSession(mUri, this); 187 mSessionManager.notifyTaskQueued(mUri); 188 onCaptureIndicatorUpdate(placeholder, 0); 189 } 190 191 @Override 192 public synchronized void startSession(byte[] placeholder, CharSequence progressMessage) { 193 if (mIsFinished) { 194 return; 195 } 196 197 mProgressMessage = progressMessage; 198 199 mPlaceHolderSession = mPlaceholderManager.insertPlaceholder(mTitle, placeholder, 200 mSessionStartMillis); 201 mUri = mPlaceHolderSession.outputUri; 202 mSessionManager.putSession(mUri, this); 203 mSessionManager.notifyTaskQueued(mUri); 204 Bitmap placeholderBitmap = Storage.getPlacerHolderForSession(mUri); 205 onCaptureIndicatorUpdate(placeholderBitmap, 0); 206 } 207 208 @Override 209 public synchronized void startSession(Uri uri, CharSequence progressMessage) { 210 mUri = uri; 211 mProgressMessage = progressMessage; 212 mPlaceHolderSession = mPlaceholderManager.convertToPlaceholder(uri); 213 214 mSessionManager.putSession(mUri, this); 215 mSessionManager.notifyTaskQueued(mUri); 216 } 217 218 @Override 219 public synchronized void cancel() { 220 if (isStarted()) { 221 mSessionManager.removeSession(mUri.toString()); 222 } 223 } 224 225 @Override 226 public synchronized void saveAndFinish(byte[] data, int width, int height, int orientation, 227 ExifInterface exif, final MediaSaver.OnMediaSavedListener listener) { 228 mIsFinished = true; 229 if (mPlaceHolderSession == null) { 230 mMediaSaver.addImage( 231 data, mTitle, mSessionStartMillis, mLocation, width, height, 232 orientation, exif, listener); 233 return; 234 } 235 mContentUri = mPlaceholderManager.finishPlaceholder(mPlaceHolderSession, mLocation, 236 orientation, exif, data, width, height, FilmstripItemData.MIME_TYPE_JPEG); 237 238 mSessionManager.removeSession(mUri.toString()); 239 mSessionManager.notifyTaskDone(mPlaceHolderSession.outputUri); 240 } 241 242 @Override 243 public StackSaver getStackSaver() { 244 return mStackSaver; 245 } 246 247 @Override 248 public void finish() { 249 if (mPlaceHolderSession == null) { 250 throw new IllegalStateException( 251 "Cannot call finish without calling startSession first."); 252 } 253 254 mIsFinished = true; 255 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { 256 @Override 257 public void run() { 258 byte[] jpegDataTemp; 259 if (mTempOutputFile.isUsable()) { 260 try { 261 jpegDataTemp = FileUtil.readFileToByteArray(mTempOutputFile.getFile()); 262 } catch (IOException e) { 263 return; 264 } 265 } else { 266 return; 267 } 268 final byte[] jpegData = jpegDataTemp; 269 270 BitmapFactory.Options options = new BitmapFactory.Options(); 271 options.inJustDecodeBounds = true; 272 BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, options); 273 int width = options.outWidth; 274 int height = options.outHeight; 275 int rotation = 0; 276 ExifInterface exif = null; 277 try { 278 exif = new ExifInterface(); 279 exif.readExif(jpegData); 280 } catch (IOException e) { 281 Log.w(TAG, "Could not read exif", e); 282 exif = null; 283 } 284 CaptureSessionImpl.this.saveAndFinish(jpegData, width, height, rotation, exif, 285 null); 286 } 287 }); 288 289 } 290 291 @Override 292 public TemporarySessionFile getTempOutputFile() { 293 return mTempOutputFile; 294 } 295 296 @Override 297 public Uri getUri() { 298 return mUri; 299 } 300 301 @Override 302 public void updatePreview() { 303 final File path; 304 if (mTempOutputFile.isUsable()) { 305 path = mTempOutputFile.getFile(); 306 } else { 307 Log.e(TAG, "Cannot update preview"); 308 return; 309 } 310 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { 311 @Override 312 public void run() { 313 byte[] jpegDataTemp; 314 try { 315 jpegDataTemp = FileUtil.readFileToByteArray(path); 316 } catch (IOException e) { 317 return; 318 } 319 final byte[] jpegData = jpegDataTemp; 320 321 BitmapFactory.Options options = new BitmapFactory.Options(); 322 Bitmap placeholder = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, 323 options); 324 mPlaceholderManager.replacePlaceholder(mPlaceHolderSession, placeholder); 325 mSessionManager.notifySessionUpdated(mUri); 326 } 327 }); 328 } 329 330 @Override 331 public void finishWithFailure(CharSequence reason) { 332 if (mPlaceHolderSession == null) { 333 throw new IllegalStateException( 334 "Cannot call finish without calling startSession first."); 335 } 336 mProgressMessage = reason; 337 mSessionManager.removeSession(mUri.toString()); 338 mSessionManager.putErrorMessage(mPlaceHolderSession.outputUri, reason); 339 mSessionManager.notifyTaskFailed(mPlaceHolderSession.outputUri, reason); 340 } 341 342 @Override 343 public void addProgressListener(ProgressListener listener) { 344 listener.onStatusMessageChanged(mProgressMessage); 345 listener.onProgressChanged(mProgressPercent); 346 mProgressListeners.add(listener); 347 } 348 349 @Override 350 public void removeProgressListener(ProgressListener listener) { 351 mProgressListeners.remove(listener); 352 } 353 354 355 private void onCaptureIndicatorUpdate(Bitmap indicator, int rotationDegrees) { 356 mSessionManager.notifySessionCaptureIndicatorAvailable(indicator, rotationDegrees); 357 } 358 359 private boolean isStarted() { 360 return mUri != null; 361 } 362} 363