CaptureSessionImpl.java revision f0ff1a9aa44d04f680af7cec8b97c3b01d3e76bd
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 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 Bitmap placeholderBitmap = Storage.getPlacerHolderForSession(mUri); 208 onCaptureIndicatorUpdate(placeholderBitmap, 0); 209 } 210 211 @Override 212 public synchronized void startSession(Uri uri, CharSequence progressMessage) { 213 mUri = uri; 214 mProgressMessage = progressMessage; 215 mPlaceHolderSession = mPlaceholderManager.convertToPlaceholder(uri); 216 217 mSessionManager.putSession(mUri, this); 218 mSessionNotifier.notifyTaskQueued(mUri); 219 } 220 221 @Override 222 public synchronized void cancel() { 223 if (isStarted()) { 224 mSessionNotifier.removeSession(mUri.toString()); 225 } 226 } 227 228 @Override 229 public synchronized void saveAndFinish(byte[] data, int width, int height, int orientation, 230 ExifInterface exif, final MediaSaver.OnMediaSavedListener listener) { 231 mIsFinished = true; 232 if (mPlaceHolderSession == null) { 233 mMediaSaver.addImage( 234 data, mTitle, mSessionStartMillis, mLocation, width, height, 235 orientation, exif, listener); 236 return; 237 } 238 mContentUri = mPlaceholderManager.finishPlaceholder(mPlaceHolderSession, mLocation, 239 orientation, exif, data, width, height, FilmstripItemData.MIME_TYPE_JPEG); 240 241 mSessionNotifier.removeSession(mUri.toString()); 242 mSessionNotifier.notifyTaskDone(mPlaceHolderSession.outputUri); 243 } 244 245 @Override 246 public StackSaver getStackSaver() { 247 return mStackSaver; 248 } 249 250 @Override 251 public void finish() { 252 if (mPlaceHolderSession == null) { 253 throw new IllegalStateException( 254 "Cannot call finish without calling startSession first."); 255 } 256 257 mIsFinished = true; 258 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { 259 @Override 260 public void run() { 261 byte[] jpegDataTemp; 262 if (mTempOutputFile.isUsable()) { 263 try { 264 jpegDataTemp = FileUtil.readFileToByteArray(mTempOutputFile.getFile()); 265 } catch (IOException e) { 266 return; 267 } 268 } else { 269 return; 270 } 271 final byte[] jpegData = jpegDataTemp; 272 273 BitmapFactory.Options options = new BitmapFactory.Options(); 274 options.inJustDecodeBounds = true; 275 BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, options); 276 int width = options.outWidth; 277 int height = options.outHeight; 278 int rotation = 0; 279 ExifInterface exif = null; 280 try { 281 exif = new ExifInterface(); 282 exif.readExif(jpegData); 283 } catch (IOException e) { 284 Log.w(TAG, "Could not read exif", e); 285 exif = null; 286 } 287 CaptureSessionImpl.this.saveAndFinish(jpegData, width, height, rotation, exif, 288 null); 289 } 290 }); 291 292 } 293 294 @Override 295 public TemporarySessionFile getTempOutputFile() { 296 return mTempOutputFile; 297 } 298 299 @Override 300 public Uri getUri() { 301 return mUri; 302 } 303 304 @Override 305 public void updatePreview() { 306 final File path; 307 if (mTempOutputFile.isUsable()) { 308 path = mTempOutputFile.getFile(); 309 } else { 310 Log.e(TAG, "Cannot update preview"); 311 return; 312 } 313 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { 314 @Override 315 public void run() { 316 byte[] jpegDataTemp; 317 try { 318 jpegDataTemp = FileUtil.readFileToByteArray(path); 319 } catch (IOException e) { 320 return; 321 } 322 final byte[] jpegData = jpegDataTemp; 323 324 BitmapFactory.Options options = new BitmapFactory.Options(); 325 Bitmap placeholder = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, 326 options); 327 mPlaceholderManager.replacePlaceholder(mPlaceHolderSession, placeholder); 328 mSessionNotifier.notifySessionUpdated(mUri); 329 } 330 }); 331 } 332 333 @Override 334 public void finishWithFailure(CharSequence reason) { 335 if (mPlaceHolderSession == null) { 336 throw new IllegalStateException( 337 "Cannot call finish without calling startSession first."); 338 } 339 mProgressMessage = reason; 340 mSessionNotifier.removeSession(mUri.toString()); 341 mSessionManager.putErrorMessage(mPlaceHolderSession.outputUri, reason); 342 mSessionNotifier.notifyTaskFailed(mPlaceHolderSession.outputUri, reason); 343 } 344 345 @Override 346 public void addProgressListener(ProgressListener listener) { 347 listener.onStatusMessageChanged(mProgressMessage); 348 listener.onProgressChanged(mProgressPercent); 349 mProgressListeners.add(listener); 350 } 351 352 @Override 353 public void removeProgressListener(ProgressListener listener) { 354 mProgressListeners.remove(listener); 355 } 356 357 358 private void onCaptureIndicatorUpdate(Bitmap indicator, int rotationDegrees) { 359 mSessionNotifier.notifySessionCaptureIndicatorAvailable(indicator, rotationDegrees); 360 } 361 362 private boolean isStarted() { 363 return mUri != null; 364 } 365} 366