1dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling/*
2dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Copyright (C) 2014 The Android Open Source Project
3dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *
4dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Licensed under the Apache License, Version 2.0 (the "License");
5dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * you may not use this file except in compliance with the License.
6dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * You may obtain a copy of the License at
7dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *
8dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *      http://www.apache.org/licenses/LICENSE-2.0
9dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *
10dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Unless required by applicable law or agreed to in writing, software
11dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * distributed under the License is distributed on an "AS IS" BASIS,
12dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * See the License for the specific language governing permissions and
14dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * limitations under the License.
15dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */
16dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
17dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingpackage com.android.camera.processing;
18dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
19dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.content.Context;
20dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.content.Intent;
215596b4c902dcb685928b43678f428746ca5ffd08Angus Kong
225596b4c902dcb685928b43678f428746ca5ffd08Angus Kongimport com.android.camera.debug.Log;
23dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
24dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport java.util.LinkedList;
25dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
26dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling/**
27dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Manages a queue of processing tasks as well as the processing service
28dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * lifecycle.
29dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * <p>
30dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Clients should only use this class and not the {@link ProcessingService}
31dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * directly.
32dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */
33dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingpublic class ProcessingServiceManager {
345596b4c902dcb685928b43678f428746ca5ffd08Angus Kong    private static final Log.Tag TAG = new Log.Tag("ProcessingSvcMgr");
35dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
36dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /** The singleton instance of this manager. */
37dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private static ProcessingServiceManager sInstance;
38dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
39dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /** The application context. */
40dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private final Context mAppContext;
41dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
42dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /** Queue of tasks to be processed. */
43dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private final LinkedList<ProcessingTask> mQueue = new LinkedList<ProcessingTask>();
44dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
45dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /** Whether a processing service is currently running. */
46dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private volatile boolean mServiceRunning = false;
47dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
48366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    /** Can be set to prevent tasks from being processed until released.*/
49366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    private boolean mHoldProcessing = false;
50366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling
51dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /**
52dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * Initializes the singleton instance.
53dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     *
54dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * @param context the application context.
55dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     */
56dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    public static void initSingleton(Context appContext) {
57dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        sInstance = new ProcessingServiceManager(appContext);
58dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
59dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
60dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /**
61dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * Note: Make sure to call {@link #initSingleton(Context)} first.
62dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     *
63dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * @return the singleton instance of the processing service manager.
64dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     */
65dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    public static ProcessingServiceManager getInstance() {
66dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        if (sInstance == null) {
67dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            throw new IllegalStateException("initSingleton() not yet called.");
68dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        }
69dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        return sInstance;
70dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
71dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
72dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private ProcessingServiceManager(Context context) {
73dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        mAppContext = context;
74dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
75dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
76dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /**
77dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * Enqueues a new task. If the service is not already running, it will be
78dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * started.
79dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     *
80dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * @param task The task to be enqueued.
81dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     */
82dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    public synchronized void enqueueTask(ProcessingTask task) {
83dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        mQueue.add(task);
84dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        Log.d(TAG, "Task added. Queue size now: " + mQueue.size());
85dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
86366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        if (!mServiceRunning && !mHoldProcessing) {
87366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            startService();
88dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        }
89dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
90dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
91dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /**
92dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * Remove the next task from the queue and return it.
93dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     *
94366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * @return The next Task or <code>null</code>, if no more tasks are in the
95366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     *         queue or we have a processing hold. If null is returned the
96366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     *         service is has to shut down as a new service is started if either
97366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     *         new items enter the queue or the processing is resumed.
98dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     */
99dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    public synchronized ProcessingTask popNextSession() {
100366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        if (!mQueue.isEmpty() && !mHoldProcessing) {
101366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            Log.d(TAG, "Popping a session. Remaining: " + (mQueue.size() - 1));
102dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            return mQueue.remove();
103366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        } else {
104366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            Log.d(TAG, "Popping null. On hold? " + mHoldProcessing);
105366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            mServiceRunning = false;
106366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            // Returning null will shut-down the service.
107dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            return null;
108dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        }
109dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
110dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
111dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /**
112366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * @return Whether the service has queued items or is running.
113366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     */
114366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    public synchronized boolean isRunningOrHasItems() {
115366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        return mServiceRunning || !mQueue.isEmpty();
116366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    }
117366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling
118366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    /**
119366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * If the queue is currently empty, processing is suspended for new incoming
120366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * items until the hold is released.
121366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * <p>
122366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * If items are in the queue, processing cannot be suspended.
123366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     *
124366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * @return Whether processing was suspended.
125366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     */
126366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    public synchronized boolean suspendProcessing() {
127366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        if (!isRunningOrHasItems()) {
128366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            Log.d(TAG, "Suspend processing");
129366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            mHoldProcessing = true;
130366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            return true;
131366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        } else {
132366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling          Log.d(TAG, "Not able to suspend processing.");
133366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling          return false;
134366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        }
135366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    }
136366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling
137366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    /**
138366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * Releases an existing hold.
1399f834952cbef4d8c2dbf67be77157192f7a70d90Carlos Hernandez     */
140366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    public synchronized void resumeProcessing() {
141366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        Log.d(TAG, "Resume processing. Queue size: " + mQueue.size());
142366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        if (mHoldProcessing) {
143366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling          mHoldProcessing = false;
144366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            if (!mQueue.isEmpty()) {
145366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling                startService();
146366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling            }
147366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        }
1489f834952cbef4d8c2dbf67be77157192f7a70d90Carlos Hernandez    }
1499f834952cbef4d8c2dbf67be77157192f7a70d90Carlos Hernandez
1509f834952cbef4d8c2dbf67be77157192f7a70d90Carlos Hernandez    /**
151366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * Starts the service which will then work through the queue. Once the queue
152366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * is empty {@link #popNextSession()} returns null), the task will kill
153366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling     * itself automatically and call #stitchingFinished().
154dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     */
155366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling    private void startService() {
156366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        mAppContext.startService(new Intent(mAppContext, ProcessingService.class));
157366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling        mServiceRunning = true;
158dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
159dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling}
160