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