/* * 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.imagebackend; import android.net.Uri; import com.android.camera.debug.Log; import com.android.camera.one.v2.camera2proxy.ImageProxy; import com.google.common.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.annotation.Nullable; /** * Implements the ability for the object to send events to multiple listeners in * a thread-safe manner. Also, listeners can also filter messages based on the a * specific image result. *

* TODO: Replace this object with a more generic listener class. TODO: Replace * the image filter code with something more efficient. */ public class ImageProcessorProxyListener implements ImageProcessorListener { private final static Log.Tag TAG = new Log.Tag("IProxyListener"); private final List mRegisteredListeners; private final HashMap mImageFilter; /** * Wrapper for the log to avoid direct references to Android Log objects * that will crash unit tests. Subclasses may override this method for * debugging. * * @param message */ protected void logWrapper(String message) { // Uncomment for more verbose messaging. // Log.v(TAG, message); } ImageProcessorProxyListener() { mRegisteredListeners = new ArrayList(); mImageFilter = new HashMap(); } /** * Returns the size of the ImageFilter so that we ensure that there are no * reference leaks. * * @return the number of elements in the mapping between * ImageProcessorListener and their ids. */ @VisibleForTesting public int getMapSize() { synchronized (mRegisteredListeners) { return mImageFilter.size(); } } /** * Returns the number of ImageProcessorListener held by the system so that * we ensure that there are no reference leaks. * * @return the number of registered ImageProcessorListener */ @VisibleForTesting public int getNumRegisteredListeners() { synchronized (mRegisteredListeners) { return mRegisteredListeners.size(); } } /** * Register a listener filtered by a particular image object. If image is * null, then events from all image processing will be sent to the * registered listener. * * @param listener The listener to be registered. * @param image The specific image to filter the events to the listener. If * null, then the listener receives events from all images that * are being processed. */ public void registerListener(ImageProcessorListener listener, @Nullable ImageProxy image) { synchronized (mRegisteredListeners) { logWrapper("There are " + mRegisteredListeners.size() + " listeners before addition"); if (!mRegisteredListeners.contains(listener)) { mRegisteredListeners.add(listener); logWrapper("Listener will be overwritten."); } if (image == null) { mImageFilter.put(listener, null); } else { mImageFilter.put(listener, image.getTimestamp()); } logWrapper("There are " + mRegisteredListeners.size() + " listeners after addition"); } return; } private List filteredListeners(long imageId) { List filteredList = new ArrayList(); for (ImageProcessorListener l : mRegisteredListeners) { if (mImageFilter.get(l) == null || mImageFilter.get(l) == imageId) { filteredList.add(l); } } return filteredList; } public void unregisterListener(ImageProcessorListener listener) { synchronized (mRegisteredListeners) { if (mRegisteredListeners.contains(listener)) { mRegisteredListeners.remove(listener); mImageFilter.remove(listener); logWrapper("There are " + mRegisteredListeners.size() + " listeners after removal"); } else { logWrapper("Couldn't find listener. There are " + mRegisteredListeners.size() + " listeners after removal"); } } } public void onStart(TaskImageContainer.TaskInfo job) { final List listeners; synchronized (mRegisteredListeners) { listeners = filteredListeners(job.contentId); } for (ImageProcessorListener l : listeners) { l.onStart(job); } } public void onResultCompressed(TaskImageContainer.TaskInfo job, TaskImageContainer.CompressedPayload payload) { final List listeners; synchronized (mRegisteredListeners) { listeners = filteredListeners(job.contentId); } for (ImageProcessorListener l : listeners) { l.onResultCompressed(job, payload); } } public void onResultUncompressed(TaskImageContainer.TaskInfo job, TaskImageContainer.UncompressedPayload payload) { final List listeners; synchronized (mRegisteredListeners) { listeners = filteredListeners(job.contentId); } for (ImageProcessorListener l : listeners) { l.onResultUncompressed(job, payload); } } public void onResultUri(TaskImageContainer.TaskInfo job, Uri uri) { final List listeners; synchronized (mRegisteredListeners) { listeners = filteredListeners(job.contentId); } for (ImageProcessorListener l : listeners) { l.onResultUri(job, uri); } } }