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