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