CaptureSessionManagerImpl.java revision 0460ef28c6970d646260584b7df0b2b3157a67ee
1/* 2 * Copyright (C) 2013 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.location.Location; 21import android.net.Uri; 22 23import com.android.camera.async.MainThread; 24import com.android.camera.debug.Log; 25 26import java.io.File; 27import java.io.IOException; 28import java.util.HashMap; 29import java.util.LinkedList; 30import java.util.Map; 31 32/** 33 * Implementation for the {@link CaptureSessionManager}. 34 * <p> 35 * Basic usage: 36 * <ul> 37 * <li>Create a new capture session.</li> 38 * <li>Pass it around to anywhere where the status of a session needs to be 39 * updated.</li> 40 * <li>If this is a longer operation, use one of the start* methods to indicate 41 * that processing of this session has started. The Camera app right now will 42 * use this to add a new item to the filmstrip and indicate the current 43 * progress.</li> 44 * <li>If the final result is already available and no processing is required, 45 * store the final image using saveAndFinish</li> 46 * <li>For longer operations, update the thumbnail and status message using the 47 * provided methods.</li> 48 * <li>For longer operations, update the thumbnail and status message using the 49 * provided methods.</li> 50 * <li>Once processing is done, the final image can be saved using saveAndFinish 51 * </li> 52 * </ul> 53 * </p> 54 * It's OK to call saveAndFinish either before or after the session has been 55 * started. 56 * <p> 57 * If startSession is called after the session has been finished, it will be 58 * treated as a no-op. 59 * </p> 60 */ 61public class CaptureSessionManagerImpl implements CaptureSessionManager { 62 63 private final class SessionNotifierImpl implements SessionNotifier { 64 /** 65 * Notifies all task listeners that the task with the given URI has been 66 * queued. 67 */ 68 @Override 69 public void notifyTaskQueued(final Uri uri) { 70 mMainHandler.execute(new Runnable() { 71 @Override 72 public void run() { 73 synchronized (mTaskListeners) { 74 for (SessionListener listener : mTaskListeners) { 75 listener.onSessionQueued(uri); 76 } 77 } 78 } 79 }); 80 } 81 82 /** 83 * Notifies all task listeners that the task with the given URI has been 84 * finished. 85 */ 86 @Override 87 public void notifyTaskDone(final Uri uri) { 88 mMainHandler.execute(new Runnable() { 89 @Override 90 public void run() { 91 synchronized (mTaskListeners) { 92 for (SessionListener listener : mTaskListeners) { 93 listener.onSessionDone(uri); 94 } 95 } 96 finalizeSession(uri); 97 } 98 }); 99 } 100 101 /** 102 * Notifies all task listeners that the task with the given URI has been 103 * failed to process. 104 */ 105 @Override 106 public void notifyTaskFailed(final Uri uri, final int failureMessageId, 107 final boolean removeFromFilmstrip) { 108 mMainHandler.execute(new Runnable() { 109 @Override 110 public void run() { 111 synchronized (mTaskListeners) { 112 for (SessionListener listener : mTaskListeners) { 113 listener.onSessionFailed(uri, failureMessageId, removeFromFilmstrip); 114 } 115 } 116 finalizeSession(uri); 117 } 118 }); 119 } 120 121 /** 122 * Notifies all task listeners that the task with the given URI has 123 * progressed to the given state. 124 */ 125 @Override 126 public void notifyTaskProgress(final Uri uri, final int progressPercent) { 127 mMainHandler.execute(new Runnable() { 128 @Override 129 public void run() { 130 synchronized (mTaskListeners) { 131 for (SessionListener listener : mTaskListeners) { 132 listener.onSessionProgress(uri, progressPercent); 133 } 134 } 135 } 136 }); 137 } 138 139 /** 140 * Notifies all task listeners that the task with the given URI has 141 * changed its progress message. 142 */ 143 @Override 144 public void notifyTaskProgressText(final Uri uri, final int messageId) { 145 mMainHandler.execute(new Runnable() { 146 @Override 147 public void run() { 148 synchronized (mTaskListeners) { 149 for (SessionListener listener : mTaskListeners) { 150 listener.onSessionProgressText(uri, messageId); 151 } 152 } 153 } 154 }); 155 } 156 157 /** 158 * Notifies all task listeners that the media associated with the task 159 * has been updated. 160 */ 161 @Override 162 public void notifySessionUpdated(final Uri uri) { 163 mMainHandler.execute(new Runnable() { 164 @Override 165 public void run() { 166 synchronized (mTaskListeners) { 167 for (SessionListener listener : mTaskListeners) { 168 listener.onSessionUpdated(uri); 169 } 170 } 171 } 172 }); 173 } 174 175 /** 176 * Notifies all task listeners that the task with the given URI has 177 * updated its media. 178 * 179 * @param indicator the bitmap that should be used for the capture 180 * indicator 181 * @param rotationDegrees the rotation of the updated preview 182 */ 183 @Override 184 public void notifySessionCaptureIndicatorAvailable(final Bitmap indicator, final int 185 rotationDegrees) { 186 mMainHandler.execute(new Runnable() { 187 @Override 188 public void run() { 189 synchronized (mTaskListeners) { 190 for (SessionListener listener : mTaskListeners) { 191 listener.onSessionCaptureIndicatorUpdate(indicator, rotationDegrees); 192 } 193 } 194 } 195 }); 196 } 197 198 @Override 199 public void notifySessionThumbnailAvailable(final Bitmap thumbnail) { 200 mMainHandler.execute(new Runnable() { 201 @Override 202 public void run() { 203 synchronized (mTaskListeners) { 204 for (SessionListener listener : mTaskListeners) { 205 listener.onSessionThumbnailUpdate(thumbnail); 206 } 207 } 208 } 209 }); 210 } 211 212 @Override 213 public void notifySessionPictureDataAvailable( 214 final byte[] pictureData, final int orientation) { 215 mMainHandler.execute(new Runnable() { 216 @Override 217 public void run() { 218 synchronized (mTaskListeners) { 219 for (SessionListener listener : mTaskListeners) { 220 listener.onSessionPictureDataUpdate(pictureData, orientation); 221 } 222 } 223 } 224 }); 225 } 226 } 227 228 private static final Log.Tag TAG = new Log.Tag("CaptureSessMgrImpl"); 229 230 /** Sessions in progress, keyed by URI. */ 231 private final Map<String, CaptureSession> mSessions; 232 private final SessionNotifier mSessionNotifier; 233 private final CaptureSessionFactory mSessionFactory; 234 private final SessionStorageManager mSessionStorageManager; 235 /** Used to fire events to the session listeners from the main thread. */ 236 private final MainThread mMainHandler; 237 238 /** Failed session messages. Uri -> message ID. */ 239 private final HashMap<Uri, Integer> mFailedSessionMessages = new HashMap<>(); 240 241 /** Listeners interested in task update events. */ 242 private final LinkedList<SessionListener> mTaskListeners = new LinkedList<SessionListener>(); 243 244 /** 245 * Initializes a new {@link CaptureSessionManager} implementation. 246 * 247 * @param sessionFactory used to create new capture session objects. 248 * @param sessionStorageManager used to tell modules where to store 249 * temporary session data 250 * @param mainHandler the main handler which listener callback is executed on. 251 */ 252 public CaptureSessionManagerImpl( 253 CaptureSessionFactory sessionFactory, 254 SessionStorageManager sessionStorageManager, 255 MainThread mainHandler) { 256 mSessionFactory = sessionFactory; 257 mSessions = new HashMap<>(); 258 mSessionNotifier = new SessionNotifierImpl(); 259 mSessionStorageManager = sessionStorageManager; 260 mMainHandler = mainHandler; 261 } 262 263 @Override 264 public CaptureSession createNewSession(String title, long sessionStartMillis, Location location) { 265 return mSessionFactory.createNewSession(this, mSessionNotifier, title, sessionStartMillis, 266 location); 267 } 268 269 @Override 270 public void putSession(Uri sessionUri, CaptureSession session) { 271 synchronized (mSessions) { 272 mSessions.put(sessionUri.toString(), session); 273 } 274 } 275 276 @Override 277 public CaptureSession getSession(Uri sessionUri) { 278 synchronized (mSessions) { 279 return mSessions.get(sessionUri.toString()); 280 } 281 } 282 283 @Override 284 public CaptureSession removeSession(Uri sessionUri) { 285 synchronized (mSessions) { 286 return mSessions.remove(sessionUri.toString()); 287 } 288 } 289 290 @Override 291 public void addSessionListener(SessionListener listener) { 292 synchronized (mTaskListeners) { 293 mTaskListeners.add(listener); 294 } 295 } 296 297 @Override 298 public void removeSessionListener(SessionListener listener) { 299 synchronized (mTaskListeners) { 300 mTaskListeners.remove(listener); 301 } 302 } 303 304 @Override 305 public File getSessionDirectory(String subDirectory) throws IOException { 306 return mSessionStorageManager.getSessionDirectory(subDirectory); 307 } 308 309 @Override 310 public boolean hasErrorMessage(Uri uri) { 311 return mFailedSessionMessages.containsKey(uri); 312 } 313 314 @Override 315 public int getErrorMessageId(Uri uri) { 316 Integer messageId = mFailedSessionMessages.get(uri); 317 if (messageId != null) { 318 return messageId; 319 } 320 return -1; 321 } 322 323 @Override 324 public void removeErrorMessage(Uri uri) { 325 mFailedSessionMessages.remove(uri); 326 } 327 328 @Override 329 public void putErrorMessage(Uri uri, int failureMessageId) { 330 mFailedSessionMessages.put(uri, failureMessageId); 331 } 332 333 @Override 334 public void fillTemporarySession(final SessionListener listener) { 335 mMainHandler.execute(new Runnable() { 336 @Override 337 public void run() { 338 synchronized (mSessions) { 339 for (String sessionUri : mSessions.keySet()) { 340 CaptureSession session = mSessions.get(sessionUri); 341 listener.onSessionQueued(session.getUri()); 342 listener.onSessionProgress(session.getUri(), session.getProgress()); 343 listener.onSessionProgressText(session.getUri(), 344 session.getProgressMessageId()); 345 } 346 } 347 } 348 }); 349 } 350 351 /** 352 * When done with a session, remove it from internal map and finalize it. 353 * 354 * @param uri Uri of the session to remove and finalize 355 */ 356 private void finalizeSession(Uri uri) { 357 CaptureSession session; 358 synchronized (mSessions) { 359 session = removeSession(uri); 360 } 361 if (session != null) { 362 session.finalize(); 363 } 364 } 365} 366