blob: c065a110d92842d331c2c74b132ce14c2cb023c8 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.camera.processing;
import android.content.Context;
import android.content.Intent;
import com.android.camera.debug.Log;
import java.util.LinkedList;
/**
* Manages a queue of processing tasks as well as the processing service
* lifecycle.
* <p>
* Clients should only use this class and not the {@link ProcessingService}
* directly.
*/
public class ProcessingServiceManager {
private static final Log.Tag TAG = new Log.Tag("ProcessingSvcMgr");
/** The singleton instance of this manager. */
private static ProcessingServiceManager sInstance;
/** The application context. */
private final Context mAppContext;
/** Queue of tasks to be processed. */
private final LinkedList<ProcessingTask> mQueue = new LinkedList<ProcessingTask>();
/** Whether a processing service is currently running. */
private volatile boolean mServiceRunning = false;
/** Can be set to prevent tasks from being processed until released.*/
private boolean mHoldProcessing = false;
/**
* Initializes the singleton instance.
*
* @param context the application context.
*/
public static void initSingleton(Context appContext) {
sInstance = new ProcessingServiceManager(appContext);
}
/**
* Note: Make sure to call {@link #initSingleton(Context)} first.
*
* @return the singleton instance of the processing service manager.
*/
public static ProcessingServiceManager getInstance() {
if (sInstance == null) {
throw new IllegalStateException("initSingleton() not yet called.");
}
return sInstance;
}
private ProcessingServiceManager(Context context) {
mAppContext = context;
}
/**
* Enqueues a new task. If the service is not already running, it will be
* started.
*
* @param task The task to be enqueued.
*/
public synchronized void enqueueTask(ProcessingTask task) {
mQueue.add(task);
Log.d(TAG, "Task added. Queue size now: " + mQueue.size());
if (!mServiceRunning && !mHoldProcessing) {
startService();
}
}
/**
* Remove the next task from the queue and return it.
*
* @return The next Task or <code>null</code>, if no more tasks are in the
* queue or we have a processing hold. If null is returned the
* service is has to shut down as a new service is started if either
* new items enter the queue or the processing is resumed.
*/
public synchronized ProcessingTask popNextSession() {
if (!mQueue.isEmpty() && !mHoldProcessing) {
Log.d(TAG, "Popping a session. Remaining: " + (mQueue.size() - 1));
return mQueue.remove();
} else {
Log.d(TAG, "Popping null. On hold? " + mHoldProcessing);
mServiceRunning = false;
// Returning null will shut-down the service.
return null;
}
}
/**
* @return Whether the service has queued items or is running.
*/
public synchronized boolean isRunningOrHasItems() {
return mServiceRunning || !mQueue.isEmpty();
}
/**
* If the queue is currently empty, processing is suspended for new incoming
* items until the hold is released.
* <p>
* If items are in the queue, processing cannot be suspended.
*
* @return Whether processing was suspended.
*/
public synchronized boolean suspendProcessing() {
if (!isRunningOrHasItems()) {
Log.d(TAG, "Suspend processing");
mHoldProcessing = true;
return true;
} else {
Log.d(TAG, "Not able to suspend processing.");
return false;
}
}
/**
* Releases an existing hold.
*/
public synchronized void resumeProcessing() {
Log.d(TAG, "Resume processing. Queue size: " + mQueue.size());
if (mHoldProcessing) {
mHoldProcessing = false;
if (!mQueue.isEmpty()) {
startService();
}
}
}
/**
* Starts the service which will then work through the queue. Once the queue
* is empty {@link #popNextSession()} returns null), the task will kill
* itself automatically and call #stitchingFinished().
*/
private void startService() {
mAppContext.startService(new Intent(mAppContext, ProcessingService.class));
mServiceRunning = true;
}
}