| /* |
| * 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.ex.camera2.utils; |
| |
| import android.hardware.camera2.CameraAccessException; |
| import android.hardware.camera2.CameraDevice; |
| import android.hardware.camera2.CaptureRequest; |
| import android.hardware.camera2.CaptureRequest.Builder; |
| import android.hardware.camera2.CaptureRequest.Key; |
| import android.view.Surface; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Objects; |
| |
| /** |
| * A set of settings to be used when filing a {@link CaptureRequest}. |
| */ |
| public class Camera2RequestSettingsSet { |
| private final Map<Key<?>, Object> mDictionary; |
| private long mRevision; |
| |
| /** |
| * Create a new instance with no settings defined. |
| * |
| * <p>Creating a request from this object without first specifying any |
| * properties on it is equivalent to just creating a request directly |
| * from the template of choice. Its revision identifier is initially |
| * {@code 0}, and will remain thus until its first modification.</p> |
| */ |
| public Camera2RequestSettingsSet() { |
| mDictionary = new HashMap<>(); |
| mRevision = 0; |
| } |
| |
| /** |
| * Perform a deep copy of the defined settings and revision number. |
| * |
| * @param other The reference instance. |
| * |
| * @throws NullPointerException If {@code other} is {@code null}. |
| */ |
| public Camera2RequestSettingsSet(Camera2RequestSettingsSet other) { |
| if (other == null) { |
| throw new NullPointerException("Tried to copy null Camera2RequestSettingsSet"); |
| } |
| |
| mDictionary = new HashMap<>(other.mDictionary); |
| mRevision = other.mRevision; |
| } |
| |
| /** |
| * Specify a setting, potentially overriding the template's default choice. |
| * |
| * <p>Providing a {@code null} {@code value} will indicate a forced use of |
| * the template's selection for that {@code key}; the difference here is |
| * that this information will be propagated with unions as documented in |
| * {@link #union}. This method increments the revision identifier if the new |
| * choice is different than the existing selection.</p> |
| * |
| * @param key Which setting to alter. |
| * @param value The new selection for that setting, or {@code null} to force |
| * the use of the template's default selection for this field. |
| * @return Whether the settings were updated, which only occurs if the |
| * {@code value} is different from any already stored. |
| * |
| * @throws NullPointerException If {@code key} is {@code null}. |
| */ |
| public <T> boolean set(Key<T> key, T value) { |
| if (key == null) { |
| throw new NullPointerException("Received a null key"); |
| } |
| |
| Object currentValue = get(key); |
| // Only save the value if it's different from the one we already have |
| if (!mDictionary.containsKey(key) || !Objects.equals(value, currentValue)) { |
| mDictionary.put(key, value); |
| ++mRevision; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Unsets a setting, preventing it from being propagated with unions or from |
| * overriding the default when creating a capture request. |
| * |
| * <p>This method increments the revision identifier if a selection had |
| * previously been made for that parameter.</p> |
| * |
| * @param key Which setting to reset. |
| * @return Whether the settings were updated, which only occurs if the |
| * specified setting already had a value or was forced to default. |
| * |
| * @throws NullPointerException If {@code key} is {@code null}. |
| */ |
| public boolean unset(Key<?> key) { |
| if (key == null) { |
| throw new NullPointerException("Received a null key"); |
| } |
| |
| if (mDictionary.containsKey(key)) { |
| mDictionary.remove(key); |
| ++mRevision; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Interrogate the current specialization of a setting. |
| * |
| * @param key Which setting to check. |
| * @return The current selection for that setting, or {@code null} if the |
| * setting is unset or forced to the template-defined default. |
| * |
| * @throws NullPointerException If {@code key} is {@code null}. |
| */ |
| @SuppressWarnings("unchecked") |
| public <T> T get(Key<T> key) { |
| if (key == null) { |
| throw new NullPointerException("Received a null key"); |
| } |
| return (T) mDictionary.get(key); |
| } |
| |
| /** |
| * Query this instance for whether it prefers a particular choice for the |
| * given request parameter. |
| * |
| * <p>This method can be used to detect whether a particular field is forced |
| * to its default value or simply unset. While {@link #get} will return |
| * {@code null} in both these cases, this method will return {@code true} |
| * and {@code false}, respectively.</p> |
| * |
| * @param key Which setting to look for. |
| * @return Whether that setting has a value that will propagate with unions. |
| * |
| * @throws NullPointerException If {@code key} is {@code null}. |
| */ |
| public boolean contains(Key<?> key) { |
| if (key == null) { |
| throw new NullPointerException("Received a null key"); |
| } |
| return mDictionary.containsKey(key); |
| } |
| |
| /** |
| * Check whether the value of the specified setting matches the given one. |
| * |
| * <p>This method uses the {@code T} type's {@code equals} method, but is |
| * {@code null}-tolerant.</p> |
| * |
| * @param key Which of this class's settings to check. |
| * @param value Value to test for equality against. |
| * @return Whether they are the same. |
| */ |
| public <T> boolean matches(Key<T> key, T value) { |
| return Objects.equals(get(key), value); |
| } |
| |
| /** |
| * Get this set of settings's revision identifier, which can be compared |
| * against cached past values to determine whether it has been modified. |
| * |
| * <p>Distinct revisions across the same object do not necessarily indicate |
| * that the object's key/value pairs have changed at all, but the same |
| * revision on the same object does imply that they've stayed the same.</p> |
| * |
| * @return The number of modifications made since the beginning of this |
| * object's heritage. |
| */ |
| public long getRevision() { |
| return mRevision; |
| } |
| |
| /** |
| * Add all settings choices defined by {@code moreSettings} to this object. |
| * |
| * <p>For any settings defined in both, the choice stored in the argument |
| * to this method take precedence. Unset settings are not propagated, but |
| * those forced to default as described in {@link set} are also forced to |
| * default in {@code this} set. Invoking this method increments {@code this} |
| * object's revision counter, but leaves the argument's unchanged.</p> |
| * |
| * @param moreSettings The source of the additional settings ({@code null} |
| * is allowed here). |
| * @return Whether these settings were updated, which can only fail if the |
| * target itself is also given as the argument. |
| */ |
| public boolean union(Camera2RequestSettingsSet moreSettings) { |
| if (moreSettings == null || moreSettings == this) { |
| return false; |
| } |
| |
| mDictionary.putAll(moreSettings.mDictionary); |
| ++mRevision; |
| return true; |
| } |
| |
| /** |
| * Create a {@link CaptureRequest} specialized for the specified |
| * {@link CameraDevice} and targeting the given {@link Surface}s. |
| * |
| * @param camera The camera from which to capture. |
| * @param template A {@link CaptureRequest} template defined in |
| * {@link CameraDevice}. |
| * @param targets The location(s) to draw the resulting image onto. |
| * @return The request, ready to be passed to the camera framework. |
| * |
| * @throws CameraAccessException Upon an underlying framework API failure. |
| * @throws NullPointerException If any argument is {@code null}. |
| */ |
| public CaptureRequest createRequest(CameraDevice camera, int template, Surface... targets) |
| throws CameraAccessException { |
| if (camera == null) { |
| throw new NullPointerException("Tried to create request using null CameraDevice"); |
| } |
| |
| Builder reqBuilder = camera.createCaptureRequest(template); |
| for (Key<?> key : mDictionary.keySet()) { |
| setRequestFieldIfNonNull(reqBuilder, key); |
| } |
| for (Surface target : targets) { |
| if (target == null) { |
| throw new NullPointerException("Tried to add null Surface as request target"); |
| } |
| reqBuilder.addTarget(target); |
| } |
| return reqBuilder.build(); |
| } |
| |
| private <T> void setRequestFieldIfNonNull(Builder requestBuilder, Key<T> key) { |
| T value = get(key); |
| if (value != null) { |
| requestBuilder.set(key, value); |
| } |
| } |
| } |