1 /*
2  * Copyright (C) 2014 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.processing;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 
22 import com.android.camera.debug.Log;
23 import com.android.camera.processing.imagebackend.ImageBackend;
24 import com.android.camera.util.AndroidContext;
25 import com.android.camera2.R;
26 
27 import java.util.LinkedList;
28 
29 /**
30  * Manages a queue of processing tasks as well as the processing service
31  * lifecycle.
32  * <p>
33  * Clients should only use this class and not the {@link ProcessingService}
34  * directly.
35  */
36 public class ProcessingServiceManager implements ProcessingTaskConsumer {
37     private static final Log.Tag TAG = new Log.Tag("ProcessingSvcMgr");
38 
39     private static class Singleton {
40         private static final ProcessingServiceManager INSTANCE = new ProcessingServiceManager(
41               AndroidContext.instance().get());
42     }
43 
instance()44     public static ProcessingServiceManager instance() {
45         return Singleton.INSTANCE;
46     }
47 
48     /** The application context. */
49     private final Context mAppContext;
50 
51     /** Queue of tasks to be processed. */
52     private final LinkedList<ProcessingTask> mQueue = new LinkedList<ProcessingTask>();
53 
54     /** Whether a processing service is currently running. */
55     private volatile boolean mServiceRunning = false;
56 
57     /** Can be set to prevent tasks from being processed until released.*/
58     private boolean mHoldProcessing = false;
59 
60     private final ImageBackend mImageBackend;
61 
ProcessingServiceManager(Context context)62     private ProcessingServiceManager(Context context) {
63         mAppContext = context;
64 
65         // Read and set the round thumbnail diameter value from resources.
66         int tinyThumbnailSize = context.getResources()
67               .getDimensionPixelSize(R.dimen.rounded_thumbnail_diameter_max);
68         mImageBackend = new ImageBackend(this, tinyThumbnailSize);
69     }
70 
71     /**
72      * Enqueues a new task. If the service is not already running, it will be
73      * started.
74      *
75      * @param task The task to be enqueued.
76      */
77     @Override
enqueueTask(ProcessingTask task)78     public synchronized void enqueueTask(ProcessingTask task) {
79         mQueue.add(task);
80         Log.d(TAG, "Task added. Queue size now: " + mQueue.size());
81 
82         if (!mServiceRunning && !mHoldProcessing) {
83             startService();
84         }
85     }
86 
87     /**
88      * Remove the next task from the queue and return it.
89      *
90      * @return The next Task or <code>null</code>, if no more tasks are in the
91      *         queue or we have a processing hold. If null is returned the
92      *         service is has to shut down as a new service is started if either
93      *         new items enter the queue or the processing is resumed.
94      */
popNextSession()95     public synchronized ProcessingTask popNextSession() {
96         if (!mQueue.isEmpty() && !mHoldProcessing) {
97             Log.d(TAG, "Popping a session. Remaining: " + (mQueue.size() - 1));
98             return mQueue.remove();
99         } else {
100             Log.d(TAG, "Popping null. On hold? " + mHoldProcessing);
101             mServiceRunning = false;
102             // Returning null will shut-down the service.
103             return null;
104         }
105     }
106 
107     /**
108      * @return Whether the service has queued items or is running.
109      */
isRunningOrHasItems()110     public synchronized boolean isRunningOrHasItems() {
111         return mServiceRunning || !mQueue.isEmpty();
112     }
113 
114     /**
115      * If the queue is currently empty, processing is suspended for new incoming
116      * items until the hold is released.
117      * <p>
118      * If items are in the queue, processing cannot be suspended.
119      *
120      * @return Whether processing was suspended.
121      */
suspendProcessing()122     public synchronized boolean suspendProcessing() {
123         if (!isRunningOrHasItems()) {
124             Log.d(TAG, "Suspend processing");
125             mHoldProcessing = true;
126             return true;
127         } else {
128           Log.d(TAG, "Not able to suspend processing.");
129           return false;
130         }
131     }
132 
133     /**
134      * Releases an existing hold.
135      */
resumeProcessing()136     public synchronized void resumeProcessing() {
137         Log.d(TAG, "Resume processing. Queue size: " + mQueue.size());
138         if (mHoldProcessing) {
139           mHoldProcessing = false;
140             if (!mQueue.isEmpty()) {
141                 startService();
142             }
143         }
144     }
145 
146     /**
147      * @return the currently defined image backend for this service.
148      */
getImageBackend()149     public ImageBackend getImageBackend() {
150         return mImageBackend;
151     }
152 
153     /**
154      * Starts the service which will then work through the queue. Once the queue
155      * is empty {@link #popNextSession()} returns null), the task will kill
156      * itself automatically and call #stitchingFinished().
157      */
startService()158     private void startService() {
159         mAppContext.startService(new Intent(mAppContext, ProcessingService.class));
160         mServiceRunning = true;
161     }
162 }
163