Merge "Move Refocus' processing service to common code." into gb-ub-photos-denali
diff --git a/src/com/android/camera/app/CameraApp.java b/src/com/android/camera/app/CameraApp.java
index 07cd322..bb27cc9 100644
--- a/src/com/android/camera/app/CameraApp.java
+++ b/src/com/android/camera/app/CameraApp.java
@@ -20,6 +20,7 @@
import android.content.Context;
import com.android.camera.MediaSaverImpl;
+import com.android.camera.processing.ProcessingServiceManager;
import com.android.camera.session.CaptureSessionManager;
import com.android.camera.session.CaptureSessionManagerImpl;
import com.android.camera.session.PlaceholderManager;
@@ -45,6 +46,8 @@
CameraUtil.initialize(this);
Context context = getApplicationContext();
+ ProcessingServiceManager.initSingleton(context);
+
mMediaSaver = new MediaSaverImpl();
mNotificationManager = new ProcessingNotificationManager(this);
mPlaceHolderManager = new PlaceholderManager(context);
diff --git a/src/com/android/camera/processing/ProcessingService.java b/src/com/android/camera/processing/ProcessingService.java
new file mode 100644
index 0000000..75c030f
--- /dev/null
+++ b/src/com/android/camera/processing/ProcessingService.java
@@ -0,0 +1,145 @@
+/*
+ * 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.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.camera.app.CameraApp;
+import com.android.camera.app.CameraServices;
+import com.android.camera.session.CaptureSessionManager;
+
+/**
+ * A service that processes a {@code ProcessingTask}. The service uses a fifo
+ * queue so that only one {@code ProcessingTask} is processed at a time.
+ * <p>
+ * The service is meant to be called via {@code ProcessingService.addTask},
+ * which takes care of starting the service and enqueueing the
+ * {@code ProcessingTask} task:
+ *
+ * <pre>
+ * {@code
+ * ProcessingTask task = new MyProcessingTask(...);
+ * ProcessingService.addTask(task);
+ * }
+ * </pre>
+ */
+public class ProcessingService extends Service {
+ private static final String TAG = "ProcessingService";
+ private static final int THREAD_PRIORITY = Process.THREAD_PRIORITY_DISPLAY;
+ private WakeLock mWakeLock;
+
+ /** Manages the capture session. */
+ private CaptureSessionManager mSessionManager;
+
+ private ProcessingServiceManager mProcessingServiceManager;
+ private Thread mProcessingThread;
+
+ @Override
+ public void onCreate() {
+ Log.d(TAG, "Starting up");
+
+ mProcessingServiceManager = ProcessingServiceManager.getInstance();
+ mSessionManager = getServices().getCaptureSessionManager();
+
+ // Keep CPU awake while allowing screen and keyboard to switch off.
+ PowerManager powerManager = (PowerManager) getSystemService(
+ Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock.acquire();
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "Shutting down");
+
+ // Tell the manager that we're shutting down, so in case new tasks are
+ // enqueued, we a new service needs to be started.
+ mProcessingServiceManager.notifyStitchingFinished();
+
+ // TODO: Cancel session in progress...
+
+ // Unlock the power manager, i.e. let power management kick in if
+ // needed.
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ asyncProcessAllTasksAndShutdown();
+
+ // We want this service to continue running until it is explicitly
+ // stopped, so return sticky.
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // We don't provide binding, so return null.
+ return null;
+ }
+
+ /**
+ * Starts a thread to process all tasks. When no more tasks are in the
+ * queue, it exits the thread and shuts down the service.
+ */
+ private void asyncProcessAllTasksAndShutdown() {
+ if (mProcessingThread != null) {
+ return;
+ }
+ mProcessingThread = new Thread() {
+ @Override
+ public void run() {
+ // Set the thread priority
+ android.os.Process.setThreadPriority(THREAD_PRIORITY);
+
+ ProcessingTask task;
+ while ((task = mProcessingServiceManager.popNextSession()) != null) {
+ processAndNotify(task);
+ }
+ stopSelf();
+ }
+ };
+ mProcessingThread.start();
+ }
+
+ /**
+ * Processes a {@code ProcessingTask} and updates the notification bar.
+ */
+ void processAndNotify(ProcessingTask task) {
+ if (task == null) {
+ Log.e(TAG, "Reference to ProcessingTask is null");
+ return;
+ }
+ task.process(this, mSessionManager.createNewSession(task.getName()));
+ }
+
+ /**
+ * Returns the common camera services.
+ */
+ private CameraServices getServices() {
+ return (CameraApp) this.getApplication();
+ }
+}
diff --git a/src/com/android/camera/processing/ProcessingServiceManager.java b/src/com/android/camera/processing/ProcessingServiceManager.java
new file mode 100644
index 0000000..c4a0fa0
--- /dev/null
+++ b/src/com/android/camera/processing/ProcessingServiceManager.java
@@ -0,0 +1,112 @@
+/*
+ * 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 android.util.Log;
+
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+/**
+ * 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 {
+ private static final String TAG = "ProcessingServiceManager";
+
+ /** The singleton instance of this manager. */
+ private static ProcessingServiceManager sInstance;
+
+ /** 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;
+
+ /**
+ * Initializes the singleton instance.
+ *
+ * @param context the application context.
+ */
+ public static void initSingleton(Context appContext) {
+ sInstance = new ProcessingServiceManager(appContext);
+ }
+
+ /**
+ * Note: Make sure to call {@link #initSingleton(Context)} first.
+ *
+ * @return the singleton instance of the processing service manager.
+ */
+ public static ProcessingServiceManager getInstance() {
+ if (sInstance == null) {
+ throw new IllegalStateException("initSingleton() not yet called.");
+ }
+ return sInstance;
+ }
+
+ private ProcessingServiceManager(Context context) {
+ mAppContext = context;
+ }
+
+ /**
+ * Enqueues a new task. If the service is not already running, it will be
+ * started.
+ *
+ * @param task The task to be enqueued.
+ */
+ public synchronized void enqueueTask(ProcessingTask task) {
+ mQueue.add(task);
+ Log.d(TAG, "Task added. Queue size now: " + mQueue.size());
+
+ if (!mServiceRunning) {
+ // Starts the service which will then work through the queue. Once
+ // the queue is empty (#popNextSession() returns null), the task
+ // will kill itself automatically and call #stitchingFinished().
+ mAppContext.startService(new Intent(mAppContext, ProcessingService.class));
+ }
+ mServiceRunning = true;
+ }
+
+ /**
+ * Remove the next task from the queue and return it.
+ *
+ * @return The next Task or <code>null</code>, of no more tasks are in the
+ * queue.
+ */
+ public synchronized ProcessingTask popNextSession() {
+ try {
+ return mQueue.remove();
+ } catch (NoSuchElementException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Called by the processing service, notifying us that it has finished.
+ */
+ public synchronized void notifyStitchingFinished() {
+ this.mServiceRunning = false;
+ }
+}
diff --git a/src/com/android/camera/processing/ProcessingTask.java b/src/com/android/camera/processing/ProcessingTask.java
new file mode 100644
index 0000000..47cb4f2
--- /dev/null
+++ b/src/com/android/camera/processing/ProcessingTask.java
@@ -0,0 +1,74 @@
+/*
+ * 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 com.android.camera.session.CaptureSession;
+
+/**
+ * An interface for tasks to be processed by a {@code ProcessingService}.
+ */
+public interface ProcessingTask {
+ /**
+ * The result returned by a {@code ProcessingTask}.
+ */
+ public class ProcessingResult {
+ public final boolean mSuccess;
+ public final CaptureSession mSession;
+
+ /**
+ * @param success whether the processing was successful.
+ * @param session the capture session for the processed task.
+ */
+ public ProcessingResult(boolean success, CaptureSession session) {
+ mSuccess = success;
+ mSession = session;
+ }
+ }
+
+ /**
+ * Classes implementing this interface can be informed when a task is done
+ * processing.
+ */
+ public interface ProcessingTaskDoneListener {
+ /**
+ * Called when a task is done processing.
+ *
+ * @param result the processing result.
+ */
+ public void onDone(ProcessingResult result);
+ }
+
+ /**
+ * Processes the given task. This will be usually called by a service.
+ *
+ * @param context the caller {@code Context}
+ * @param session the {@code CaptureSession}
+ * @return the {@code ProcessResult} with the result of the processing
+ */
+ public ProcessingResult process(Context context, CaptureSession session);
+
+ /**
+ * @return the name of the task. It can be null to indicate that the task
+ * has no name.
+ */
+ public String getName();
+
+ /** Sets a listener that is informed when this task is done processing. */
+ public void setDoneListener(ProcessingTaskDoneListener listener);
+}