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