• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ex.camera2.utils;
18 
19 import android.hardware.camera2.CameraAccessException;
20 import android.hardware.camera2.CameraDevice;
21 import android.hardware.camera2.CaptureRequest;
22 import android.hardware.camera2.CaptureRequest.Builder;
23 import android.hardware.camera2.CaptureRequest.Key;
24 import android.view.Surface;
25 
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Objects;
29 
30 /**
31  * A set of settings to be used when filing a {@link CaptureRequest}.
32  */
33 public 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      */
Camera2RequestSettingsSet()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      */
Camera2RequestSettingsSet(Camera2RequestSettingsSet other)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      */
set(Key<T> key, T value)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      */
unset(Key<?> key)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")
get(Key<T> key)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      */
contains(Key<?> key)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      */
matches(Key<T> key, T value)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      */
getRevision()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      */
union(Camera2RequestSettingsSet moreSettings)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      */
createRequest(CameraDevice camera, int template, Surface... targets)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 
setRequestFieldIfNonNull(Builder requestBuilder, Key<T> key)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