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 17package com.android.ex.camera2.utils; 18 19import android.hardware.camera2.CameraAccessException; 20import android.hardware.camera2.CameraDevice; 21import android.hardware.camera2.CaptureRequest; 22import android.hardware.camera2.CaptureRequest.Builder; 23import android.hardware.camera2.CaptureRequest.Key; 24import android.view.Surface; 25 26import java.util.HashMap; 27import java.util.Map; 28import java.util.Objects; 29 30/** 31 * A set of settings to be used when filing a {@link CaptureRequest}. 32 */ 33public class Camera2RequestSettingsSet { 34 private final Map<Key<?>, Object> mDictionary; 35 private long mRevision; 36 37 /** 38 * Create a new instance with no settings defined. 39 * 40 * <p>Creating a request from this object without first specifying any 41 * properties on it is equivalent to just creating a request directly 42 * from the template of choice. Its revision identifier is initially 43 * {@code 0}, and will remain thus until its first modification.</p> 44 */ 45 public Camera2RequestSettingsSet() { 46 mDictionary = new HashMap<>(); 47 mRevision = 0; 48 } 49 50 /** 51 * Perform a deep copy of the defined settings and revision number. 52 * 53 * @param other The reference instance. 54 * 55 * @throws NullPointerException If {@code other} is {@code null}. 56 */ 57 public Camera2RequestSettingsSet(Camera2RequestSettingsSet other) { 58 if (other == null) { 59 throw new NullPointerException("Tried to copy null Camera2RequestSettingsSet"); 60 } 61 62 mDictionary = new HashMap<>(other.mDictionary); 63 mRevision = other.mRevision; 64 } 65 66 /** 67 * Specify a setting, potentially overriding the template's default choice. 68 * 69 * <p>Providing a {@code null} {@code value} will indicate a forced use of 70 * the template's selection for that {@code key}; the difference here is 71 * that this information will be propagated with unions as documented in 72 * {@link #union}. This method increments the revision identifier if the new 73 * choice is different than the existing selection.</p> 74 * 75 * @param key Which setting to alter. 76 * @param value The new selection for that setting, or {@code null} to force 77 * the use of the template's default selection for this field. 78 * @return Whether the settings were updated, which only occurs if the 79 * {@code value} is different from any already stored. 80 * 81 * @throws NullPointerException If {@code key} is {@code null}. 82 */ 83 public <T> boolean set(Key<T> key, T value) { 84 if (key == null) { 85 throw new NullPointerException("Received a null key"); 86 } 87 88 Object currentValue = get(key); 89 // Only save the value if it's different from the one we already have 90 if (!mDictionary.containsKey(key) || !Objects.equals(value, currentValue)) { 91 mDictionary.put(key, value); 92 ++mRevision; 93 return true; 94 } 95 return false; 96 } 97 98 /** 99 * Unsets a setting, preventing it from being propagated with unions or from 100 * overriding the default when creating a capture request. 101 * 102 * <p>This method increments the revision identifier if a selection had 103 * previously been made for that parameter.</p> 104 * 105 * @param key Which setting to reset. 106 * @return Whether the settings were updated, which only occurs if the 107 * specified setting already had a value or was forced to default. 108 * 109 * @throws NullPointerException If {@code key} is {@code null}. 110 */ 111 public boolean unset(Key<?> key) { 112 if (key == null) { 113 throw new NullPointerException("Received a null key"); 114 } 115 116 if (mDictionary.containsKey(key)) { 117 mDictionary.remove(key); 118 ++mRevision; 119 return true; 120 } 121 return false; 122 } 123 124 /** 125 * Interrogate the current specialization of a setting. 126 * 127 * @param key Which setting to check. 128 * @return The current selection for that setting, or {@code null} if the 129 * setting is unset or forced to the template-defined default. 130 * 131 * @throws NullPointerException If {@code key} is {@code null}. 132 */ 133 @SuppressWarnings("unchecked") 134 public <T> T get(Key<T> key) { 135 if (key == null) { 136 throw new NullPointerException("Received a null key"); 137 } 138 return (T) mDictionary.get(key); 139 } 140 141 /** 142 * Query this instance for whether it prefers a particular choice for the 143 * given request parameter. 144 * 145 * <p>This method can be used to detect whether a particular field is forced 146 * to its default value or simply unset. While {@link #get} will return 147 * {@code null} in both these cases, this method will return {@code true} 148 * and {@code false}, respectively.</p> 149 * 150 * @param key Which setting to look for. 151 * @return Whether that setting has a value that will propagate with unions. 152 * 153 * @throws NullPointerException If {@code key} is {@code null}. 154 */ 155 public boolean contains(Key<?> key) { 156 if (key == null) { 157 throw new NullPointerException("Received a null key"); 158 } 159 return mDictionary.containsKey(key); 160 } 161 162 /** 163 * Check whether the value of the specified setting matches the given one. 164 * 165 * <p>This method uses the {@code T} type's {@code equals} method, but is 166 * {@code null}-tolerant.</p> 167 * 168 * @param key Which of this class's settings to check. 169 * @param value Value to test for equality against. 170 * @return Whether they are the same. 171 */ 172 public <T> boolean matches(Key<T> key, T value) { 173 return Objects.equals(get(key), value); 174 } 175 176 /** 177 * Get this set of settings's revision identifier, which can be compared 178 * against cached past values to determine whether it has been modified. 179 * 180 * <p>Distinct revisions across the same object do not necessarily indicate 181 * that the object's key/value pairs have changed at all, but the same 182 * revision on the same object does imply that they've stayed the same.</p> 183 * 184 * @return The number of modifications made since the beginning of this 185 * object's heritage. 186 */ 187 public long getRevision() { 188 return mRevision; 189 } 190 191 /** 192 * Add all settings choices defined by {@code moreSettings} to this object. 193 * 194 * <p>For any settings defined in both, the choice stored in the argument 195 * to this method take precedence. Unset settings are not propagated, but 196 * those forced to default as described in {@link set} are also forced to 197 * default in {@code this} set. Invoking this method increments {@code this} 198 * object's revision counter, but leaves the argument's unchanged.</p> 199 * 200 * @param moreSettings The source of the additional settings ({@code null} 201 * is allowed here). 202 * @return Whether these settings were updated, which can only fail if the 203 * target itself is also given as the argument. 204 */ 205 public boolean union(Camera2RequestSettingsSet moreSettings) { 206 if (moreSettings == null || moreSettings == this) { 207 return false; 208 } 209 210 mDictionary.putAll(moreSettings.mDictionary); 211 ++mRevision; 212 return true; 213 } 214 215 /** 216 * Create a {@link CaptureRequest} specialized for the specified 217 * {@link CameraDevice} and targeting the given {@link Surface}s. 218 * 219 * @param camera The camera from which to capture. 220 * @param template A {@link CaptureRequest} template defined in 221 * {@link CameraDevice}. 222 * @param targets The location(s) to draw the resulting image onto. 223 * @return The request, ready to be passed to the camera framework. 224 * 225 * @throws CameraAccessException Upon an underlying framework API failure. 226 * @throws NullPointerException If any argument is {@code null}. 227 */ 228 public CaptureRequest createRequest(CameraDevice camera, int template, Surface... targets) 229 throws CameraAccessException { 230 if (camera == null) { 231 throw new NullPointerException("Tried to create request using null CameraDevice"); 232 } 233 234 Builder reqBuilder = camera.createCaptureRequest(template); 235 for (Key<?> key : mDictionary.keySet()) { 236 setRequestFieldIfNonNull(reqBuilder, key); 237 } 238 for (Surface target : targets) { 239 if (target == null) { 240 throw new NullPointerException("Tried to add null Surface as request target"); 241 } 242 reqBuilder.addTarget(target); 243 } 244 return reqBuilder.build(); 245 } 246 247 private <T> void setRequestFieldIfNonNull(Builder requestBuilder, Key<T> key) { 248 T value = get(key); 249 if (value != null) { 250 requestBuilder.set(key, value); 251 } 252 } 253} 254