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.imagebackend; 18 19 import android.net.Uri; 20 21 import com.android.camera.debug.Log; 22 import com.android.camera.one.v2.camera2proxy.ImageProxy; 23 24 import com.google.common.annotations.VisibleForTesting; 25 26 import java.util.ArrayList; 27 import java.util.HashMap; 28 import java.util.List; 29 30 import javax.annotation.Nullable; 31 32 /** 33 * Implements the ability for the object to send events to multiple listeners in 34 * a thread-safe manner. Also, listeners can also filter messages based on the a 35 * specific image result. 36 * <p> 37 * TODO: Replace this object with a more generic listener class. TODO: Replace 38 * the image filter code with something more efficient. 39 */ 40 public class ImageProcessorProxyListener implements ImageProcessorListener { 41 42 private final static Log.Tag TAG = new Log.Tag("IProxyListener"); 43 44 private final List<ImageProcessorListener> mRegisteredListeners; 45 46 private final HashMap<ImageProcessorListener, Long> mImageFilter; 47 48 /** 49 * Wrapper for the log to avoid direct references to Android Log objects 50 * that will crash unit tests. Subclasses may override this method for 51 * debugging. 52 * 53 * @param message 54 */ 55 protected void logWrapper(String message) { 56 // Uncomment for more verbose messaging. 57 // Log.v(TAG, message); 58 } 59 60 ImageProcessorProxyListener() { 61 mRegisteredListeners = new ArrayList<ImageProcessorListener>(); 62 mImageFilter = new HashMap<ImageProcessorListener, Long>(); 63 } 64 65 /** 66 * Returns the size of the ImageFilter so that we ensure that there are no 67 * reference leaks. 68 * 69 * @return the number of elements in the mapping between 70 * ImageProcessorListener and their ids. 71 */ 72 @VisibleForTesting 73 public int getMapSize() { 74 synchronized (mRegisteredListeners) { 75 return mImageFilter.size(); 76 } 77 } 78 79 /** 80 * Returns the number of ImageProcessorListener held by the system so that 81 * we ensure that there are no reference leaks. 82 * 83 * @return the number of registered ImageProcessorListener 84 */ 85 @VisibleForTesting 86 public int getNumRegisteredListeners() { 87 synchronized (mRegisteredListeners) { 88 return mRegisteredListeners.size(); 89 } 90 } 91 92 /** 93 * Register a listener filtered by a particular image object. If image is 94 * null, then events from all image processing will be sent to the 95 * registered listener. 96 * 97 * @param listener The listener to be registered. 98 * @param image The specific image to filter the events to the listener. If 99 * null, then the listener receives events from all images that 100 * are being processed. 101 */ 102 public void registerListener(ImageProcessorListener listener, 103 @Nullable ImageProxy image) { 104 synchronized (mRegisteredListeners) { 105 logWrapper("There are " + mRegisteredListeners.size() 106 + " listeners before addition"); 107 if (!mRegisteredListeners.contains(listener)) { 108 mRegisteredListeners.add(listener); 109 logWrapper("Listener will be overwritten."); 110 } 111 112 if (image == null) { 113 mImageFilter.put(listener, null); 114 } else { 115 mImageFilter.put(listener, image.getTimestamp()); 116 } 117 logWrapper("There are " + mRegisteredListeners.size() 118 + " listeners after addition"); 119 } 120 121 return; 122 } 123 124 private List<ImageProcessorListener> filteredListeners(long imageId) { 125 List<ImageProcessorListener> filteredList = new ArrayList<ImageProcessorListener>(); 126 127 for (ImageProcessorListener l : mRegisteredListeners) { 128 if (mImageFilter.get(l) == null || mImageFilter.get(l) == imageId) { 129 filteredList.add(l); 130 } 131 } 132 133 return filteredList; 134 } 135 136 public void unregisterListener(ImageProcessorListener listener) { 137 synchronized (mRegisteredListeners) { 138 if (mRegisteredListeners.contains(listener)) { 139 mRegisteredListeners.remove(listener); 140 mImageFilter.remove(listener); 141 logWrapper("There are " + mRegisteredListeners.size() 142 + " listeners after removal"); 143 } else { 144 logWrapper("Couldn't find listener. There are " + mRegisteredListeners.size() 145 + " listeners after removal"); 146 } 147 } 148 } 149 150 public void onStart(TaskImageContainer.TaskInfo job) { 151 final List<ImageProcessorListener> listeners; 152 synchronized (mRegisteredListeners) { 153 listeners = filteredListeners(job.contentId); 154 } 155 156 for (ImageProcessorListener l : listeners) { 157 l.onStart(job); 158 } 159 } 160 161 public void onResultCompressed(TaskImageContainer.TaskInfo job, 162 TaskImageContainer.CompressedPayload payload) { 163 final List<ImageProcessorListener> listeners; 164 synchronized (mRegisteredListeners) { 165 listeners = filteredListeners(job.contentId); 166 } 167 168 for (ImageProcessorListener l : listeners) { 169 l.onResultCompressed(job, payload); 170 } 171 } 172 173 public void onResultUncompressed(TaskImageContainer.TaskInfo job, 174 TaskImageContainer.UncompressedPayload payload) { 175 final List<ImageProcessorListener> listeners; 176 synchronized (mRegisteredListeners) { 177 listeners = filteredListeners(job.contentId); 178 } 179 180 for (ImageProcessorListener l : listeners) { 181 l.onResultUncompressed(job, payload); 182 } 183 } 184 185 public void onResultUri(TaskImageContainer.TaskInfo job, Uri uri) { 186 final List<ImageProcessorListener> listeners; 187 synchronized (mRegisteredListeners) { 188 listeners = filteredListeners(job.contentId); 189 } 190 191 for (ImageProcessorListener l : listeners) { 192 l.onResultUri(job, uri); 193 } 194 } 195 196 } 197