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.camera.one.v2.autofocus;
18
19import android.hardware.camera2.CaptureRequest;
20import android.hardware.camera2.CaptureResult;
21
22import com.android.camera.async.Updatable;
23import com.android.camera.one.v2.camera2proxy.CaptureResultProxy;
24import com.google.common.base.Objects;
25import com.google.common.collect.ImmutableSet;
26import com.google.common.util.concurrent.SettableFuture;
27
28import java.util.Set;
29import java.util.concurrent.ExecutionException;
30import java.util.concurrent.TimeUnit;
31import java.util.concurrent.TimeoutException;
32
33import javax.annotation.ParametersAreNonnullByDefault;
34
35/**
36 * Listens for image metadata and returns the result of an AE scan caused by an
37 * AE_TRIGGER_START. The result of indicates whether flash is required.
38 * <p>
39 * Maintains the current state of auto-exposure scans resulting from explicit
40 * precapture trigger requests. This maintains the subset of the finite state
41 * machine of {@link CaptureResult#CONTROL_AE_STATE} which relates to
42 * CONTROL_AE_PRECAPTURE_TRIGGER
43 * <p>
44 * That is, it invokes the given callback when a scan is complete, according to
45 * the following sequence:
46 *
47 * <pre>
48 * .* CONTROL_AE_PRECAPTURE_TRIGGER_START .*
49 * (STATE_INACTIVE|STATE_FLASH_REQUIRED|STATE_CONVERGED|STATE_LOCKED)
50 * </pre>
51 * <p>
52 * See the android documentation for {@link CaptureResult#CONTROL_AE_STATE} for
53 * further documentation on the state machine this class implements.
54 */
55@ParametersAreNonnullByDefault
56public final class AETriggerResult implements Updatable<CaptureResultProxy> {
57    private static final Set<Integer> TRIGGER_DONE_STATES = ImmutableSet.of(
58            CaptureResult.CONTROL_AE_STATE_INACTIVE,
59            CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED,
60            CaptureResult.CONTROL_AE_STATE_CONVERGED,
61            CaptureResult.CONTROL_AE_STATE_LOCKED);
62
63    private final TriggerStateMachine mStateMachine;
64    private final SettableFuture<Boolean> mFutureResult;
65
66    public AETriggerResult() {
67        mStateMachine = new TriggerStateMachine(
68                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START,
69                TRIGGER_DONE_STATES);
70        mFutureResult = SettableFuture.create();
71    }
72
73    @Override
74    public void update(CaptureResultProxy result) {
75        Integer state = result.get(CaptureResult.CONTROL_AE_STATE);
76        boolean done = mStateMachine.update(
77                result.getFrameNumber(),
78                result.getRequest().get(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER),
79                state);
80        if (done) {
81            boolean flashRequired = Objects.equal(state, CaptureResult
82                    .CONTROL_AE_STATE_FLASH_REQUIRED);
83            mFutureResult.set(flashRequired);
84        }
85    }
86
87    /**
88     * Blocks until the AE scan is complete.
89     *
90     * @return Whether the scene requires flash to be properly exposed.
91     * @throws InterruptedException
92     */
93    public boolean get() throws InterruptedException {
94        try {
95            return mFutureResult.get();
96        } catch (ExecutionException impossible) {
97            throw new RuntimeException(impossible);
98        }
99    }
100
101    /**
102     * Blocks until the AE scan is complete.
103     *
104     * @return Whether the scene requires flash to be properly exposed.
105     * @throws InterruptedException
106     */
107    public boolean get(long timeout, TimeUnit timeUnit) throws InterruptedException,
108            TimeoutException {
109        try {
110            return mFutureResult.get(timeout, timeUnit);
111        } catch (ExecutionException impossible) {
112            throw new RuntimeException(impossible);
113        }
114    }
115}
116