Home | History | Annotate | Download | only in session
      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
     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         @Override
    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
    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
    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
    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
    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
    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
    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      */
    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
    279     public CaptureSession createNewSession(String title, long sessionStartMillis, Location location) {
    280         return mSessionFactory.createNewSession(this, mSessionNotifier, title, sessionStartMillis,
    281                 location);
    282     }
    283 
    284     @Override
    285     public void putSession(Uri sessionUri, CaptureSession session) {
    286         synchronized (mSessions) {
    287             mSessions.put(sessionUri.toString(), session);
    288         }
    289     }
    290 
    291     @Override
    292     public CaptureSession getSession(Uri sessionUri) {
    293         synchronized (mSessions) {
    294             return mSessions.get(sessionUri.toString());
    295         }
    296     }
    297 
    298     @Override
    299     public CaptureSession removeSession(Uri sessionUri) {
    300         synchronized (mSessions) {
    301             return mSessions.remove(sessionUri.toString());
    302         }
    303     }
    304 
    305     @Override
    306     public void addSessionListener(SessionListener listener) {
    307         synchronized (mTaskListeners) {
    308             mTaskListeners.add(listener);
    309         }
    310     }
    311 
    312     @Override
    313     public void removeSessionListener(SessionListener listener) {
    314         synchronized (mTaskListeners) {
    315             mTaskListeners.remove(listener);
    316         }
    317     }
    318 
    319     @Override
    320     public File getSessionDirectory(String subDirectory) throws IOException {
    321         return mSessionStorageManager.getSessionDirectory(subDirectory);
    322     }
    323 
    324     @Override
    325     public boolean hasErrorMessage(Uri uri) {
    326         return mFailedSessionMessages.containsKey(uri);
    327     }
    328 
    329     @Override
    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
    339     public void removeErrorMessage(Uri uri) {
    340         mFailedSessionMessages.remove(uri);
    341     }
    342 
    343     @Override
    344     public void putErrorMessage(Uri uri, int failureMessageId) {
    345         mFailedSessionMessages.put(uri, failureMessageId);
    346     }
    347 
    348     @Override
    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      */
    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