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 17 package com.android.camera.session; 18 19 import android.graphics.Bitmap; 20 import android.location.Location; 21 import android.net.Uri; 22 23 import com.android.camera.async.MainThread; 24 import com.android.camera.debug.Log; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.util.HashMap; 29 import java.util.LinkedList; 30 import 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 */ 61 public 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 notifyTaskQueued(final Uri uri)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 notifyTaskDone(final Uri uri)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 notifyTaskFailed(final Uri uri, final int failureMessageId, final boolean removeFromFilmstrip)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 @Override notifyTaskCanceled(final Uri uri)122 public void notifyTaskCanceled(final Uri uri) { 123 mMainHandler.execute(new Runnable() { 124 @Override 125 public void run() { 126 synchronized (mTaskListeners) { 127 for (SessionListener listener : mTaskListeners) { 128 listener.onSessionCanceled(uri); 129 } 130 } 131 finalizeSession(uri); 132 } 133 }); 134 } 135 136 /** 137 * Notifies all task listeners that the task with the given URI has 138 * progressed to the given state. 139 */ 140 @Override notifyTaskProgress(final Uri uri, final int progressPercent)141 public void notifyTaskProgress(final Uri uri, final int progressPercent) { 142 mMainHandler.execute(new Runnable() { 143 @Override 144 public void run() { 145 synchronized (mTaskListeners) { 146 for (SessionListener listener : mTaskListeners) { 147 listener.onSessionProgress(uri, progressPercent); 148 } 149 } 150 } 151 }); 152 } 153 154 /** 155 * Notifies all task listeners that the task with the given URI has 156 * changed its progress message. 157 */ 158 @Override notifyTaskProgressText(final Uri uri, final int messageId)159 public void notifyTaskProgressText(final Uri uri, final int messageId) { 160 mMainHandler.execute(new Runnable() { 161 @Override 162 public void run() { 163 synchronized (mTaskListeners) { 164 for (SessionListener listener : mTaskListeners) { 165 listener.onSessionProgressText(uri, messageId); 166 } 167 } 168 } 169 }); 170 } 171 172 /** 173 * Notifies all task listeners that the media associated with the task 174 * has been updated. 175 */ 176 @Override notifySessionUpdated(final Uri uri)177 public void notifySessionUpdated(final Uri uri) { 178 mMainHandler.execute(new Runnable() { 179 @Override 180 public void run() { 181 synchronized (mTaskListeners) { 182 for (SessionListener listener : mTaskListeners) { 183 listener.onSessionUpdated(uri); 184 } 185 } 186 } 187 }); 188 } 189 190 /** 191 * Notifies all task listeners that the task with the given URI has 192 * updated its media. 193 * 194 * @param indicator the bitmap that should be used for the capture 195 * indicator 196 * @param rotationDegrees the rotation of the updated preview 197 */ 198 @Override notifySessionCaptureIndicatorAvailable(final Bitmap indicator, final int rotationDegrees)199 public void notifySessionCaptureIndicatorAvailable(final Bitmap indicator, final int 200 rotationDegrees) { 201 mMainHandler.execute(new Runnable() { 202 @Override 203 public void run() { 204 synchronized (mTaskListeners) { 205 for (SessionListener listener : mTaskListeners) { 206 listener.onSessionCaptureIndicatorUpdate(indicator, rotationDegrees); 207 } 208 } 209 } 210 }); 211 } 212 213 @Override notifySessionThumbnailAvailable(final Bitmap thumbnail)214 public void notifySessionThumbnailAvailable(final Bitmap thumbnail) { 215 mMainHandler.execute(new Runnable() { 216 @Override 217 public void run() { 218 synchronized (mTaskListeners) { 219 for (SessionListener listener : mTaskListeners) { 220 listener.onSessionThumbnailUpdate(thumbnail); 221 } 222 } 223 } 224 }); 225 } 226 227 @Override notifySessionPictureDataAvailable( final byte[] pictureData, final int orientation)228 public void notifySessionPictureDataAvailable( 229 final byte[] pictureData, final int orientation) { 230 mMainHandler.execute(new Runnable() { 231 @Override 232 public void run() { 233 synchronized (mTaskListeners) { 234 for (SessionListener listener : mTaskListeners) { 235 listener.onSessionPictureDataUpdate(pictureData, orientation); 236 } 237 } 238 } 239 }); 240 } 241 } 242 243 private static final Log.Tag TAG = new Log.Tag("CaptureSessMgrImpl"); 244 245 /** Sessions in progress, keyed by URI. */ 246 private final Map<String, CaptureSession> mSessions; 247 private final SessionNotifier mSessionNotifier; 248 private final CaptureSessionFactory mSessionFactory; 249 private final SessionStorageManager mSessionStorageManager; 250 /** Used to fire events to the session listeners from the main thread. */ 251 private final MainThread mMainHandler; 252 253 /** Failed session messages. Uri -> message ID. */ 254 private final HashMap<Uri, Integer> mFailedSessionMessages = new HashMap<>(); 255 256 /** Listeners interested in task update events. */ 257 private final LinkedList<SessionListener> mTaskListeners = new LinkedList<SessionListener>(); 258 259 /** 260 * Initializes a new {@link CaptureSessionManager} implementation. 261 * 262 * @param sessionFactory used to create new capture session objects. 263 * @param sessionStorageManager used to tell modules where to store 264 * temporary session data 265 * @param mainHandler the main handler which listener callback is executed on. 266 */ CaptureSessionManagerImpl( CaptureSessionFactory sessionFactory, SessionStorageManager sessionStorageManager, MainThread mainHandler)267 public CaptureSessionManagerImpl( 268 CaptureSessionFactory sessionFactory, 269 SessionStorageManager sessionStorageManager, 270 MainThread mainHandler) { 271 mSessionFactory = sessionFactory; 272 mSessions = new HashMap<>(); 273 mSessionNotifier = new SessionNotifierImpl(); 274 mSessionStorageManager = sessionStorageManager; 275 mMainHandler = mainHandler; 276 } 277 278 @Override createNewSession(String title, long sessionStartMillis, Location location)279 public CaptureSession createNewSession(String title, long sessionStartMillis, Location location) { 280 return mSessionFactory.createNewSession(this, mSessionNotifier, title, sessionStartMillis, 281 location); 282 } 283 284 @Override putSession(Uri sessionUri, CaptureSession session)285 public void putSession(Uri sessionUri, CaptureSession session) { 286 synchronized (mSessions) { 287 mSessions.put(sessionUri.toString(), session); 288 } 289 } 290 291 @Override getSession(Uri sessionUri)292 public CaptureSession getSession(Uri sessionUri) { 293 synchronized (mSessions) { 294 return mSessions.get(sessionUri.toString()); 295 } 296 } 297 298 @Override removeSession(Uri sessionUri)299 public CaptureSession removeSession(Uri sessionUri) { 300 synchronized (mSessions) { 301 return mSessions.remove(sessionUri.toString()); 302 } 303 } 304 305 @Override addSessionListener(SessionListener listener)306 public void addSessionListener(SessionListener listener) { 307 synchronized (mTaskListeners) { 308 mTaskListeners.add(listener); 309 } 310 } 311 312 @Override removeSessionListener(SessionListener listener)313 public void removeSessionListener(SessionListener listener) { 314 synchronized (mTaskListeners) { 315 mTaskListeners.remove(listener); 316 } 317 } 318 319 @Override getSessionDirectory(String subDirectory)320 public File getSessionDirectory(String subDirectory) throws IOException { 321 return mSessionStorageManager.getSessionDirectory(subDirectory); 322 } 323 324 @Override hasErrorMessage(Uri uri)325 public boolean hasErrorMessage(Uri uri) { 326 return mFailedSessionMessages.containsKey(uri); 327 } 328 329 @Override getErrorMessageId(Uri uri)330 public int getErrorMessageId(Uri uri) { 331 Integer messageId = mFailedSessionMessages.get(uri); 332 if (messageId != null) { 333 return messageId; 334 } 335 return -1; 336 } 337 338 @Override removeErrorMessage(Uri uri)339 public void removeErrorMessage(Uri uri) { 340 mFailedSessionMessages.remove(uri); 341 } 342 343 @Override putErrorMessage(Uri uri, int failureMessageId)344 public void putErrorMessage(Uri uri, int failureMessageId) { 345 mFailedSessionMessages.put(uri, failureMessageId); 346 } 347 348 @Override fillTemporarySession(final SessionListener listener)349 public void fillTemporarySession(final SessionListener listener) { 350 mMainHandler.execute(new Runnable() { 351 @Override 352 public void run() { 353 synchronized (mSessions) { 354 for (String sessionUri : mSessions.keySet()) { 355 CaptureSession session = mSessions.get(sessionUri); 356 listener.onSessionQueued(session.getUri()); 357 listener.onSessionProgress(session.getUri(), session.getProgress()); 358 listener.onSessionProgressText(session.getUri(), 359 session.getProgressMessageId()); 360 } 361 } 362 } 363 }); 364 } 365 366 /** 367 * When done with a session, remove it from internal map and finalize it. 368 * 369 * @param uri Uri of the session to remove and finalize 370 */ finalizeSession(Uri uri)371 private void finalizeSession(Uri uri) { 372 CaptureSession session; 373 synchronized (mSessions) { 374 session = removeSession(uri); 375 } 376 if (session != null) { 377 session.finalizeSession(); 378 } 379 } 380 } 381