| /* |
| * 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 com.android.camera.processing.imagebackend.ImageBackend; |
| import com.android.camera.util.AndroidContext; |
| import com.android.camera2.R; |
| |
| 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 implements ProcessingTaskConsumer { |
| private static final Log.Tag TAG = new Log.Tag("ProcessingSvcMgr"); |
| |
| private static class Singleton { |
| private static final ProcessingServiceManager INSTANCE = new ProcessingServiceManager( |
| AndroidContext.instance().get()); |
| } |
| |
| public static ProcessingServiceManager instance() { |
| return Singleton.INSTANCE; |
| } |
| |
| /** 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; |
| |
| private final ImageBackend mImageBackend; |
| |
| private ProcessingServiceManager(Context context) { |
| mAppContext = context; |
| |
| // Read and set the round thumbnail diameter value from resources. |
| int tinyThumbnailSize = context.getResources() |
| .getDimensionPixelSize(R.dimen.rounded_thumbnail_diameter_max); |
| mImageBackend = new ImageBackend(this, tinyThumbnailSize); |
| } |
| |
| /** |
| * Enqueues a new task. If the service is not already running, it will be |
| * started. |
| * |
| * @param task The task to be enqueued. |
| */ |
| @Override |
| 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(); |
| } |
| } |
| } |
| |
| /** |
| * @return the currently defined image backend for this service. |
| */ |
| public ImageBackend getImageBackend() { |
| return mImageBackend; |
| } |
| |
| /** |
| * 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; |
| } |
| } |