1dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling/* 2dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Copyright (C) 2014 The Android Open Source Project 3dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * 4dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Licensed under the Apache License, Version 2.0 (the "License"); 5dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * you may not use this file except in compliance with the License. 6dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * You may obtain a copy of the License at 7dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * 8dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * http://www.apache.org/licenses/LICENSE-2.0 9dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * 10dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Unless required by applicable law or agreed to in writing, software 11dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * distributed under the License is distributed on an "AS IS" BASIS, 12dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * See the License for the specific language governing permissions and 14dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * limitations under the License. 15dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */ 16dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 17dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingpackage com.android.camera.processing; 18dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 19dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.content.Context; 20dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.content.Intent; 215596b4c902dcb685928b43678f428746ca5ffd08Angus Kong 225596b4c902dcb685928b43678f428746ca5ffd08Angus Kongimport com.android.camera.debug.Log; 233830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberlingimport com.android.camera.processing.imagebackend.ImageBackend; 245c1db74d807c64e7917d927b532cd109c697ce35Paul Rohdeimport com.android.camera.util.AndroidContext; 252c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohdeimport com.android.camera2.R; 26dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 27dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport java.util.LinkedList; 28dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 29dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling/** 30dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Manages a queue of processing tasks as well as the processing service 31dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * lifecycle. 32dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * <p> 33dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Clients should only use this class and not the {@link ProcessingService} 34dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * directly. 35dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */ 36abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Linpublic class ProcessingServiceManager implements ProcessingTaskConsumer { 375596b4c902dcb685928b43678f428746ca5ffd08Angus Kong private static final Log.Tag TAG = new Log.Tag("ProcessingSvcMgr"); 38dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 395c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde private static class Singleton { 405c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde private static final ProcessingServiceManager INSTANCE = new ProcessingServiceManager( 415c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde AndroidContext.instance().get()); 425c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde } 435c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde 445c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde public static ProcessingServiceManager instance() { 455c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde return Singleton.INSTANCE; 465c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde } 47dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 48dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling /** The application context. */ 49dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling private final Context mAppContext; 50dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 51dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling /** Queue of tasks to be processed. */ 52dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling private final LinkedList<ProcessingTask> mQueue = new LinkedList<ProcessingTask>(); 53dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 54dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling /** Whether a processing service is currently running. */ 55dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling private volatile boolean mServiceRunning = false; 56dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 57366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling /** Can be set to prevent tasks from being processed until released.*/ 58366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling private boolean mHoldProcessing = false; 59366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling 605c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde private final ImageBackend mImageBackend; 6130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin 62dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling private ProcessingServiceManager(Context context) { 63dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling mAppContext = context; 642c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde 652c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde // Read and set the round thumbnail diameter value from resources. 662c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde int tinyThumbnailSize = context.getResources() 672c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde .getDimensionPixelSize(R.dimen.rounded_thumbnail_diameter_max); 682c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde mImageBackend = new ImageBackend(this, tinyThumbnailSize); 69dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling } 70dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 71dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling /** 72dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Enqueues a new task. If the service is not already running, it will be 73dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * started. 74dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * 75dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * @param task The task to be enqueued. 76dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */ 77abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin @Override 78dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling public synchronized void enqueueTask(ProcessingTask task) { 79dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling mQueue.add(task); 80dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling Log.d(TAG, "Task added. Queue size now: " + mQueue.size()); 81dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 82366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling if (!mServiceRunning && !mHoldProcessing) { 83366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling startService(); 84dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling } 85dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling } 86dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 87dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling /** 88dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Remove the next task from the queue and return it. 89dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * 90366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * @return The next Task or <code>null</code>, if no more tasks are in the 91366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * queue or we have a processing hold. If null is returned the 92366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * service is has to shut down as a new service is started if either 93366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * new items enter the queue or the processing is resumed. 94dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */ 95dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling public synchronized ProcessingTask popNextSession() { 96366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling if (!mQueue.isEmpty() && !mHoldProcessing) { 97366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling Log.d(TAG, "Popping a session. Remaining: " + (mQueue.size() - 1)); 98dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling return mQueue.remove(); 99366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling } else { 100366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling Log.d(TAG, "Popping null. On hold? " + mHoldProcessing); 101366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling mServiceRunning = false; 102366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling // Returning null will shut-down the service. 103dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling return null; 104dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling } 105dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling } 106dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling 107dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling /** 108366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * @return Whether the service has queued items or is running. 109366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling */ 110366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling public synchronized boolean isRunningOrHasItems() { 111366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling return mServiceRunning || !mQueue.isEmpty(); 112366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling } 113366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling 114366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling /** 115366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * If the queue is currently empty, processing is suspended for new incoming 116366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * items until the hold is released. 117366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * <p> 118366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * If items are in the queue, processing cannot be suspended. 119366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * 120366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * @return Whether processing was suspended. 121366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling */ 122366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling public synchronized boolean suspendProcessing() { 123366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling if (!isRunningOrHasItems()) { 124366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling Log.d(TAG, "Suspend processing"); 125366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling mHoldProcessing = true; 126366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling return true; 127366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling } else { 128366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling Log.d(TAG, "Not able to suspend processing."); 129366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling return false; 130366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling } 131366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling } 132366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling 133366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling /** 134366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * Releases an existing hold. 1359f834952cbef4d8c2dbf67be77157192f7a70d90Carlos Hernandez */ 136366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling public synchronized void resumeProcessing() { 137366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling Log.d(TAG, "Resume processing. Queue size: " + mQueue.size()); 138366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling if (mHoldProcessing) { 139366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling mHoldProcessing = false; 140366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling if (!mQueue.isEmpty()) { 141366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling startService(); 142366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling } 143366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling } 1449f834952cbef4d8c2dbf67be77157192f7a70d90Carlos Hernandez } 1459f834952cbef4d8c2dbf67be77157192f7a70d90Carlos Hernandez 1469f834952cbef4d8c2dbf67be77157192f7a70d90Carlos Hernandez /** 1475c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde * @return the currently defined image backend for this service. 1485c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde */ 1495c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde public ImageBackend getImageBackend() { 1505c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde return mImageBackend; 1515c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde } 1525c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde 1535c1db74d807c64e7917d927b532cd109c697ce35Paul Rohde /** 154366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * Starts the service which will then work through the queue. Once the queue 155366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * is empty {@link #popNextSession()} returns null), the task will kill 156366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling * itself automatically and call #stitchingFinished(). 157dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */ 158366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling private void startService() { 159366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling mAppContext.startService(new Intent(mAppContext, ProcessingService.class)); 160366c13baa640bc597cac360c5a1ffad5a3618b11Sascha Haeberling mServiceRunning = true; 161dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling } 162dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling} 163