1 /*
2  * Copyright (C) 2011 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.camera;
18 
19 import com.android.camera.ui.FaceView;
20 import com.android.camera.ui.FocusIndicator;
21 import com.android.camera.ui.FocusIndicatorView;
22 
23 import android.content.res.AssetFileDescriptor;
24 import android.graphics.Matrix;
25 import android.graphics.Rect;
26 import android.graphics.RectF;
27 import android.hardware.Camera.Area;
28 import android.hardware.Camera.Parameters;
29 import android.media.MediaActionSound;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.util.Log;
33 import android.view.MotionEvent;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.widget.RelativeLayout;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 
41 // A class that handles everything about focus in still picture mode.
42 // This also handles the metering area because it is the same as focus area.
43 public class FocusManager {
44     private static final String TAG = "FocusManager";
45 
46     private static final int RESET_TOUCH_FOCUS = 0;
47     private static final int FOCUS_BEEP_VOLUME = 100;
48     private static final int RESET_TOUCH_FOCUS_DELAY = 3000;
49 
50     private int mState = STATE_IDLE;
51     private static final int STATE_IDLE = 0; // Focus is not active.
52     private static final int STATE_FOCUSING = 1; // Focus is in progress.
53     // Focus is in progress and the camera should take a picture after focus finishes.
54     private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2;
55     private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds.
56     private static final int STATE_FAIL = 4; // Focus finishes and fails.
57 
58     private boolean mInitialized;
59     private boolean mFocusAreaSupported;
60     private boolean mLockAeAwbNeeded;
61     private boolean mAeAwbLock;
62     private Matrix mMatrix;
63     private View mFocusIndicatorRotateLayout;
64     private FocusIndicatorView mFocusIndicator;
65     private View mPreviewFrame;
66     private FaceView mFaceView;
67     private List<Area> mFocusArea; // focus area in driver format
68     private List<Area> mMeteringArea; // metering area in driver format
69     private String mFocusMode;
70     private String[] mDefaultFocusModes;
71     private String mOverrideFocusMode;
72     private Parameters mParameters;
73     private ComboPreferences mPreferences;
74     private Handler mHandler;
75     Listener mListener;
76 
77     public interface Listener {
autoFocus()78         public void autoFocus();
cancelAutoFocus()79         public void cancelAutoFocus();
capture()80         public boolean capture();
startFaceDetection()81         public void startFaceDetection();
stopFaceDetection()82         public void stopFaceDetection();
setFocusParameters()83         public void setFocusParameters();
playSound(int soundId)84         public void playSound(int soundId);
85     }
86 
87     private class MainHandler extends Handler {
88         @Override
handleMessage(Message msg)89         public void handleMessage(Message msg) {
90             switch (msg.what) {
91                 case RESET_TOUCH_FOCUS: {
92                     cancelAutoFocus();
93                     mListener.startFaceDetection();
94                     break;
95                 }
96             }
97         }
98     }
99 
FocusManager(ComboPreferences preferences, String[] defaultFocusModes)100     public FocusManager(ComboPreferences preferences, String[] defaultFocusModes) {
101         mPreferences = preferences;
102         mDefaultFocusModes = defaultFocusModes;
103         mHandler = new MainHandler();
104         mMatrix = new Matrix();
105     }
106 
107     // This has to be initialized before initialize().
initializeParameters(Parameters parameters)108     public void initializeParameters(Parameters parameters) {
109         mParameters = parameters;
110         mFocusAreaSupported = (mParameters.getMaxNumFocusAreas() > 0
111                 && isSupported(Parameters.FOCUS_MODE_AUTO,
112                         mParameters.getSupportedFocusModes()));
113         mLockAeAwbNeeded = (mParameters.isAutoExposureLockSupported() ||
114                 mParameters.isAutoWhiteBalanceLockSupported());
115     }
116 
initialize(View focusIndicatorRotate, View previewFrame, FaceView faceView, Listener listener, boolean mirror, int displayOrientation)117     public void initialize(View focusIndicatorRotate, View previewFrame,
118             FaceView faceView, Listener listener, boolean mirror, int displayOrientation) {
119         mFocusIndicatorRotateLayout = focusIndicatorRotate;
120         mFocusIndicator = (FocusIndicatorView) focusIndicatorRotate.findViewById(
121                 R.id.focus_indicator);
122         mPreviewFrame = previewFrame;
123         mFaceView = faceView;
124         mListener = listener;
125 
126         Matrix matrix = new Matrix();
127         Util.prepareMatrix(matrix, mirror, displayOrientation,
128                 previewFrame.getWidth(), previewFrame.getHeight());
129         // In face detection, the matrix converts the driver coordinates to UI
130         // coordinates. In tap focus, the inverted matrix converts the UI
131         // coordinates to driver coordinates.
132         matrix.invert(mMatrix);
133 
134         if (mParameters != null) {
135             mInitialized = true;
136         } else {
137             Log.e(TAG, "mParameters is not initialized.");
138         }
139     }
140 
onShutterDown()141     public void onShutterDown() {
142         if (!mInitialized) return;
143 
144         // Lock AE and AWB so users can half-press shutter and recompose.
145         if (mLockAeAwbNeeded && !mAeAwbLock) {
146             mAeAwbLock = true;
147             mListener.setFocusParameters();
148         }
149 
150         if (needAutoFocusCall()) {
151             // Do not focus if touch focus has been triggered.
152             if (mState != STATE_SUCCESS && mState != STATE_FAIL) {
153                 autoFocus();
154             }
155         }
156     }
157 
onShutterUp()158     public void onShutterUp() {
159         if (!mInitialized) return;
160 
161         if (needAutoFocusCall()) {
162             // User releases half-pressed focus key.
163             if (mState == STATE_FOCUSING || mState == STATE_SUCCESS
164                     || mState == STATE_FAIL) {
165                 cancelAutoFocus();
166             }
167         }
168 
169         // Unlock AE and AWB after cancelAutoFocus. Camera API does not
170         // guarantee setParameters can be called during autofocus.
171         if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) {
172             mAeAwbLock = false;
173             mListener.setFocusParameters();
174         }
175     }
176 
doSnap()177     public void doSnap() {
178         if (!mInitialized) return;
179 
180         // If the user has half-pressed the shutter and focus is completed, we
181         // can take the photo right away. If the focus mode is infinity, we can
182         // also take the photo.
183         if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) {
184             capture();
185         } else if (mState == STATE_FOCUSING) {
186             // Half pressing the shutter (i.e. the focus button event) will
187             // already have requested AF for us, so just request capture on
188             // focus here.
189             mState = STATE_FOCUSING_SNAP_ON_FINISH;
190         } else if (mState == STATE_IDLE) {
191             // We didn't do focus. This can happen if the user press focus key
192             // while the snapshot is still in progress. The user probably wants
193             // the next snapshot as soon as possible, so we just do a snapshot
194             // without focusing again.
195             capture();
196         }
197     }
198 
onShutter()199     public void onShutter() {
200         resetTouchFocus();
201         updateFocusUI();
202     }
203 
onAutoFocus(boolean focused)204     public void onAutoFocus(boolean focused) {
205         if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
206             // Take the picture no matter focus succeeds or fails. No need
207             // to play the AF sound if we're about to play the shutter
208             // sound.
209             if (focused) {
210                 mState = STATE_SUCCESS;
211             } else {
212                 mState = STATE_FAIL;
213             }
214             updateFocusUI();
215             capture();
216         } else if (mState == STATE_FOCUSING) {
217             // This happens when (1) user is half-pressing the focus key or
218             // (2) touch focus is triggered. Play the focus tone. Do not
219             // take the picture now.
220             if (focused) {
221                 mState = STATE_SUCCESS;
222                 // Do not play the sound in continuous autofocus mode. It does
223                 // not do a full scan. The focus callback arrives before doSnap
224                 // so the state is always STATE_FOCUSING.
225                 if (!Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.
226                         equals(mFocusMode)) {
227                     mListener.playSound(MediaActionSound.FOCUS_COMPLETE);
228                 }
229             } else {
230                 mState = STATE_FAIL;
231             }
232             updateFocusUI();
233             // If this is triggered by touch focus, cancel focus after a
234             // while.
235             if (mFocusArea != null) {
236                 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
237             }
238         } else if (mState == STATE_IDLE) {
239             // User has released the focus key before focus completes.
240             // Do nothing.
241         }
242     }
243 
onTouch(MotionEvent e)244     public boolean onTouch(MotionEvent e) {
245         if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) return false;
246 
247         // Let users be able to cancel previous touch focus.
248         if ((mFocusArea != null) && (mState == STATE_FOCUSING ||
249                     mState == STATE_SUCCESS || mState == STATE_FAIL)) {
250             cancelAutoFocus();
251         }
252 
253         // Initialize variables.
254         int x = Math.round(e.getX());
255         int y = Math.round(e.getY());
256         int focusWidth = mFocusIndicatorRotateLayout.getWidth();
257         int focusHeight = mFocusIndicatorRotateLayout.getHeight();
258         int previewWidth = mPreviewFrame.getWidth();
259         int previewHeight = mPreviewFrame.getHeight();
260         if (mFocusArea == null) {
261             mFocusArea = new ArrayList<Area>();
262             mFocusArea.add(new Area(new Rect(), 1));
263             mMeteringArea = new ArrayList<Area>();
264             mMeteringArea.add(new Area(new Rect(), 1));
265         }
266 
267         // Convert the coordinates to driver format.
268         // AE area is bigger because exposure is sensitive and
269         // easy to over- or underexposure if area is too small.
270         calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight,
271                 mFocusArea.get(0).rect);
272         calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight,
273                 mMeteringArea.get(0).rect);
274 
275         // Use margin to set the focus indicator to the touched area.
276         RelativeLayout.LayoutParams p =
277                 (RelativeLayout.LayoutParams) mFocusIndicatorRotateLayout.getLayoutParams();
278         int left = Util.clamp(x - focusWidth / 2, 0, previewWidth - focusWidth);
279         int top = Util.clamp(y - focusHeight / 2, 0, previewHeight - focusHeight);
280         p.setMargins(left, top, 0, 0);
281         // Disable "center" rule because we no longer want to put it in the center.
282         int[] rules = p.getRules();
283         rules[RelativeLayout.CENTER_IN_PARENT] = 0;
284         mFocusIndicatorRotateLayout.requestLayout();
285 
286         // Stop face detection because we want to specify focus and metering area.
287         mListener.stopFaceDetection();
288 
289         // Set the focus area and metering area.
290         mListener.setFocusParameters();
291         if (mFocusAreaSupported && (e.getAction() == MotionEvent.ACTION_UP)) {
292             autoFocus();
293         } else {  // Just show the indicator in all other cases.
294             updateFocusUI();
295             // Reset the metering area in 3 seconds.
296             mHandler.removeMessages(RESET_TOUCH_FOCUS);
297             mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
298         }
299 
300         return true;
301     }
302 
onPreviewStarted()303     public void onPreviewStarted() {
304         mState = STATE_IDLE;
305     }
306 
onPreviewStopped()307     public void onPreviewStopped() {
308         mState = STATE_IDLE;
309         resetTouchFocus();
310         // If auto focus was in progress, it would have been canceled.
311         updateFocusUI();
312     }
313 
onCameraReleased()314     public void onCameraReleased() {
315         onPreviewStopped();
316     }
317 
autoFocus()318     private void autoFocus() {
319         Log.v(TAG, "Start autofocus.");
320         mListener.autoFocus();
321         mState = STATE_FOCUSING;
322         // Pause the face view because the driver will keep sending face
323         // callbacks after the focus completes.
324         if (mFaceView != null) mFaceView.pause();
325         updateFocusUI();
326         mHandler.removeMessages(RESET_TOUCH_FOCUS);
327     }
328 
cancelAutoFocus()329     private void cancelAutoFocus() {
330         Log.v(TAG, "Cancel autofocus.");
331 
332         // Reset the tap area before calling mListener.cancelAutofocus.
333         // Otherwise, focus mode stays at auto and the tap area passed to the
334         // driver is not reset.
335         resetTouchFocus();
336         mListener.cancelAutoFocus();
337         if (mFaceView != null) mFaceView.resume();
338         mState = STATE_IDLE;
339         updateFocusUI();
340         mHandler.removeMessages(RESET_TOUCH_FOCUS);
341     }
342 
capture()343     private void capture() {
344         if (mListener.capture()) {
345             mState = STATE_IDLE;
346             mHandler.removeMessages(RESET_TOUCH_FOCUS);
347         }
348     }
349 
350     // This can only be called after mParameters is initialized.
getFocusMode()351     public String getFocusMode() {
352         if (mOverrideFocusMode != null) return mOverrideFocusMode;
353         List<String> supportedFocusModes = mParameters.getSupportedFocusModes();
354 
355         if (mFocusAreaSupported && mFocusArea != null) {
356             // Always use autofocus in tap-to-focus.
357             mFocusMode = Parameters.FOCUS_MODE_AUTO;
358         } else {
359             // The default is continuous autofocus.
360             mFocusMode = mPreferences.getString(
361                     CameraSettings.KEY_FOCUS_MODE, null);
362 
363             // Try to find a supported focus mode from the default list.
364             if (mFocusMode == null) {
365                 for (int i = 0; i < mDefaultFocusModes.length; i++) {
366                     String mode = mDefaultFocusModes[i];
367                     if (isSupported(mode, supportedFocusModes)) {
368                         mFocusMode = mode;
369                         break;
370                     }
371                 }
372             }
373         }
374         if (!isSupported(mFocusMode, supportedFocusModes)) {
375             // For some reasons, the driver does not support the current
376             // focus mode. Fall back to auto.
377             if (isSupported(Parameters.FOCUS_MODE_AUTO,
378                     mParameters.getSupportedFocusModes())) {
379                 mFocusMode = Parameters.FOCUS_MODE_AUTO;
380             } else {
381                 mFocusMode = mParameters.getFocusMode();
382             }
383         }
384         return mFocusMode;
385     }
386 
getFocusAreas()387     public List<Area> getFocusAreas() {
388         return mFocusArea;
389     }
390 
getMeteringAreas()391     public List<Area> getMeteringAreas() {
392         return mMeteringArea;
393     }
394 
updateFocusUI()395     public void updateFocusUI() {
396         if (!mInitialized) return;
397 
398         // Set the length of focus indicator according to preview frame size.
399         int len = Math.min(mPreviewFrame.getWidth(), mPreviewFrame.getHeight()) / 4;
400         ViewGroup.LayoutParams layout = mFocusIndicator.getLayoutParams();
401         layout.width = len;
402         layout.height = len;
403 
404         // Show only focus indicator or face indicator.
405         boolean faceExists = (mFaceView != null && mFaceView.faceExists());
406         FocusIndicator focusIndicator = (faceExists) ? mFaceView : mFocusIndicator;
407 
408         if (mState == STATE_IDLE) {
409             if (mFocusArea == null) {
410                 focusIndicator.clear();
411             } else {
412                 // Users touch on the preview and the indicator represents the
413                 // metering area. Either focus area is not supported or
414                 // autoFocus call is not required.
415                 focusIndicator.showStart();
416             }
417         } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
418             focusIndicator.showStart();
419         } else {
420             // In CAF, do not show success or failure because it only returns
421             // the focus status. It does not do a full scan. So the result is
422             // failure most of the time.
423             if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) {
424                 focusIndicator.showStart();
425             } else if (mState == STATE_SUCCESS) {
426                 focusIndicator.showSuccess();
427             } else if (mState == STATE_FAIL) {
428                 focusIndicator.showFail();
429             }
430         }
431     }
432 
resetTouchFocus()433     public void resetTouchFocus() {
434         if (!mInitialized) return;
435 
436         // Put focus indicator to the center.
437         RelativeLayout.LayoutParams p =
438                 (RelativeLayout.LayoutParams) mFocusIndicatorRotateLayout.getLayoutParams();
439         int[] rules = p.getRules();
440         rules[RelativeLayout.CENTER_IN_PARENT] = RelativeLayout.TRUE;
441         p.setMargins(0, 0, 0, 0);
442 
443         mFocusArea = null;
444         mMeteringArea = null;
445     }
446 
calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, int x, int y, int previewWidth, int previewHeight, Rect rect)447     public void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple,
448             int x, int y, int previewWidth, int previewHeight, Rect rect) {
449         int areaWidth = (int)(focusWidth * areaMultiple);
450         int areaHeight = (int)(focusHeight * areaMultiple);
451         int left = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth);
452         int top = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight);
453 
454         RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight);
455         mMatrix.mapRect(rectF);
456         Util.rectFToRect(rectF, rect);
457     }
458 
isFocusCompleted()459     public boolean isFocusCompleted() {
460         return mState == STATE_SUCCESS || mState == STATE_FAIL;
461     }
462 
isFocusingSnapOnFinish()463     public boolean isFocusingSnapOnFinish() {
464         return mState == STATE_FOCUSING_SNAP_ON_FINISH;
465     }
466 
removeMessages()467     public void removeMessages() {
468         mHandler.removeMessages(RESET_TOUCH_FOCUS);
469     }
470 
overrideFocusMode(String focusMode)471     public void overrideFocusMode(String focusMode) {
472         mOverrideFocusMode = focusMode;
473     }
474 
setAeAwbLock(boolean lock)475     public void setAeAwbLock(boolean lock) {
476         mAeAwbLock = lock;
477     }
478 
getAeAwbLock()479     public boolean getAeAwbLock() {
480         return mAeAwbLock;
481     }
482 
isSupported(String value, List<String> supported)483     private static boolean isSupported(String value, List<String> supported) {
484         return supported == null ? false : supported.indexOf(value) >= 0;
485     }
486 
needAutoFocusCall()487     private boolean needAutoFocusCall() {
488         String focusMode = getFocusMode();
489         return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY)
490                 || focusMode.equals(Parameters.FOCUS_MODE_FIXED)
491                 || focusMode.equals(Parameters.FOCUS_MODE_EDOF));
492     }
493 }
494