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