1package com.android.camera.stats;
2
3import android.graphics.Rect;
4import android.hardware.camera2.CaptureResult;
5import android.hardware.camera2.params.Face;
6import android.os.SystemClock;
7
8import com.google.common.annotations.VisibleForTesting;
9
10import com.android.camera.exif.ExifInterface;
11import com.android.camera.one.v2.camera2proxy.CaptureResultProxy;
12import com.android.camera.ui.TouchCoordinate;
13
14import java.util.ArrayList;
15import java.util.List;
16
17/**
18 * Accumulates statistics during the lifecycle of a Capture Session. Since a
19 * CaptureSession instance is available for the lifetime of the request and the
20 * image processing of the said request, CaptureSessionStatsCollector is
21 * attached to the CaptureSession so that we can collect information from both
22 * the CaptureModule and the ImageBackend.
23 */
24public class CaptureSessionStatsCollector {
25
26
27    /** Time when capture is completed in SystemClock.elapsedRealtime(). */
28    protected long mCaptureTimeMillis;
29    protected final UsageStatistics mUsageStatistics;
30
31    // Define all fields as Objects so that we know whether they were set or not.
32    // A required field
33    protected Integer mMode;
34
35    // Fields with defaults, which are passed as primitives
36    protected Boolean mIsFrontFacing = Boolean.FALSE;
37    protected Boolean mIsHdr = Boolean.FALSE;
38    protected Float mZoom = new Float(0.0f);
39
40    // Optional fields (passed as Java Objects)
41    protected String mFilename;
42    protected ExifInterface mExifInterface;
43    protected String mFlashSetting;
44    protected Boolean mGridLinesOn;
45    protected Float mTimerSeconds;
46    protected TouchCoordinate mTouchCoordinate;
47    protected Boolean mVolumeButtonShutter;
48    protected List<Camera2FaceProxy> mFaceProxies;
49    protected Float mLensFocusDistance;
50    protected Rect mActiveSensorSize;
51
52    /**
53     * Constructor
54     */
55    public CaptureSessionStatsCollector() {
56        mUsageStatistics = UsageStatistics.instance();
57    }
58
59    /**
60     * Constructor for testing/dependency injection
61     */
62    @VisibleForTesting
63    public CaptureSessionStatsCollector(UsageStatistics usageStatistics) {
64        mUsageStatistics = usageStatistics;
65    }
66
67    /**
68     * Decorate the collector when the CaptureResult becomes available, which happens sometime
69     * after picture is taken.  In the current implementation, we query this structure for
70     * two fields: 1) CaptureResult.STATISTICS_FACES and 2) CaptureResult.LENS_FOCUS_DISTANCE
71     *
72     * @param captureResult CaptureResults to be queried for capture event information
73     */
74    public void decorateAtTimeOfCaptureRequestAvailable(CaptureResultProxy captureResult) {
75        Face [] facesCaptured = captureResult.get(CaptureResult.STATISTICS_FACES);
76        if(facesCaptured == null) {
77            mFaceProxies = null;
78        } else {
79            mFaceProxies = new ArrayList<>(facesCaptured.length);
80            for (Face face : facesCaptured) {
81                mFaceProxies.add(Camera2FaceProxy.from(face));
82            }
83        }
84
85        mLensFocusDistance = captureResult.get(CaptureResult.LENS_FOCUS_DISTANCE);
86    }
87
88    /**
89     * Accumulate the information that should be available at the time of the Capture Request.
90     * If you are unable to deliver one of these parameters, you may want to think again.
91     *
92     * @param mode a mode specified by eventprotos.NavigationChange.Mode
93     * @param filename filename of image to be created
94     * @param frontFacing whether the camera request is going to the front camera or not
95     * @param isHDR whether the camera is HDR mode
96     * @param zoom value of the zoom on the camera request
97     * @param flashSetting string representing the state of the flash (KEY_FLASH_MODE)
98     * @param gridLinesOn whether the gridlines are on the preview display
99     * @param timerSeconds value of the countdown timer
100     * @param touchCoordinate the last shutter touch coordinate
101     * @param volumeButtonShutter whether the volume button was used to initialize the request.
102     * @param activeSensorSize size of the active sensor array, to be used for the coordinate
103     *                         space of the face array
104     */
105    public void decorateAtTimeCaptureRequest(
106            final int mode,
107            final String filename,
108            final boolean frontFacing,
109            final boolean isHDR,
110            final float zoom,
111            final String flashSetting,
112            final boolean gridLinesOn,
113            final float timerSeconds,
114            final TouchCoordinate touchCoordinate,
115            final Boolean volumeButtonShutter,
116            final Rect activeSensorSize
117    ) {
118        mMode = mode;
119        mFilename = filename;
120        mIsFrontFacing = frontFacing;
121        mIsHdr = isHDR;
122        mZoom = zoom;
123        mFlashSetting = flashSetting;
124        mGridLinesOn = gridLinesOn;
125        mTimerSeconds = timerSeconds;
126        mTouchCoordinate = touchCoordinate;
127        mVolumeButtonShutter = volumeButtonShutter;
128        mActiveSensorSize = activeSensorSize;
129    }
130
131    /**
132     * Accumalate the information that should be available at the time of
133     * Write-To-Disk. If you are unable to deliver one of these parameters, you
134     * may want to think again.
135     *
136     * @param exifInterface exif values to be associated with the JPEG image
137     *            file that is being created.
138     */
139    public void decorateAtTimeWriteToDisk(
140            final ExifInterface exifInterface
141    ) {
142        mExifInterface = exifInterface;
143    }
144
145    /**
146     * Called when image processing time begins.
147     */
148    public void markProcessingTimeStart() {
149        mCaptureTimeMillis = getElapsedRealTime();
150    }
151
152    /**
153     * Send capture event to the UsageStatistics singleton.
154     */
155    public void photoCaptureDoneEvent() {
156        Float processingTime = (getElapsedRealTime() - mCaptureTimeMillis) / 1000f;
157        if (isValidForPhotoCaptureEvent()) {
158            mUsageStatistics.photoCaptureDoneEvent(
159                    mMode, mFilename, mExifInterface, mIsFrontFacing,
160                    mIsHdr, mZoom, mFlashSetting, mGridLinesOn, mTimerSeconds,
161                    processingTime, mTouchCoordinate, mVolumeButtonShutter,
162                    mFaceProxies, mLensFocusDistance, mActiveSensorSize);
163        }
164    }
165
166    /**
167     * Returns whether all the fields in the CaptureSessionStatsCollector are set or not.
168     */
169    public boolean isCompleteForPhotoCaptureEvent() {
170        return (mMode != null) &&
171                (mFilename != null) &&
172                (mExifInterface != null) &&
173                (mIsFrontFacing != null) &&
174                (mIsHdr != null) &&
175                (mZoom != null) &&
176                (mFlashSetting != null) &&
177                (mGridLinesOn != null) &&
178                (mTimerSeconds != null) &&
179                (mTouchCoordinate != null) &&
180                (mVolumeButtonShutter != null);
181    }
182
183    /**
184     * Return whether state of collector is sufficient for PhotoCaptureEvent.
185     *
186     * @return whether state of collector is sufficient for PhotoCaptureEvent.
187     */
188    public boolean isValidForPhotoCaptureEvent() {
189        return (mMode != null);
190    }
191
192    /**
193     * Call to SystemClock.elapsedRealtime() that we can override for testing.
194     */
195    public long getElapsedRealTime() {
196        return SystemClock.elapsedRealtime();
197    }
198
199}
200