Home | History | Annotate | Download | only in processing
      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 
     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 
     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
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    158     private void startService() {
    159         mAppContext.startService(new Intent(mAppContext, ProcessingService.class));
    160         mServiceRunning = true;
    161     }
    162 }
    163