• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.Handler;
30 import android.os.Message;
31 import android.util.Log;
32 import android.view.MotionEvent;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.widget.RelativeLayout;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 // A class that handles everything about focus in still picture mode.
41 // This also handles the metering area because it is the same as focus area.
42 public class FocusManager {
43     private static final String TAG = "FocusManager";
44 
45     private static final int RESET_TOUCH_FOCUS = 0;
46     private static final int FOCUS_BEEP_VOLUME = 100;
47     private static final int RESET_TOUCH_FOCUS_DELAY = 3000;
48 
49     private int mState = STATE_IDLE;
50     private static final int STATE_IDLE = 0; // Focus is not active.
51     private static final int STATE_FOCUSING = 1; // Focus is in progress.
52     // Focus is in progress and the camera should take a picture after focus finishes.
53     private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2;
54     private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds.
55     private static final int STATE_FAIL = 4; // Focus finishes and fails.
56 
57     private boolean mInitialized;
58     private boolean mFocusAreaSupported;
59     private boolean mInLongPress;
60     private boolean mLockAeAwbNeeded;
61     private boolean mAeAwbLock;
62     private Matrix mMatrix;
63     private SoundPlayer mSoundPlayer;
64     private View mFocusIndicatorRotateLayout;
65     private FocusIndicatorView mFocusIndicator;
66     private View mPreviewFrame;
67     private FaceView mFaceView;
68     private List<Area> mFocusArea; // focus area in driver format
69     private List<Area> mMeteringArea; // metering area in driver format
70     private String mFocusMode;
71     private String mDefaultFocusMode;
72     private String mOverrideFocusMode;
73     private Parameters mParameters;
74     private ComboPreferences mPreferences;
75     private Handler mHandler;
76     Listener mListener;
77 
78     public interface Listener {
autoFocus()79         public void autoFocus();
cancelAutoFocus()80         public void cancelAutoFocus();
capture()81         public boolean capture();
startFaceDetection()82         public void startFaceDetection();
stopFaceDetection()83         public void stopFaceDetection();
setFocusParameters()84         public void setFocusParameters();
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 defaultFocusMode)100     public FocusManager(ComboPreferences preferences, String defaultFocusMode) {
101         mPreferences = preferences;
102         mDefaultFocusMode = defaultFocusMode;
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 
shutterLongPressed()177     public void shutterLongPressed() {
178         if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)
179                 && isSupported(Parameters.FOCUS_MODE_AUTO, mParameters.getSupportedFocusModes())) {
180             if (mState == STATE_IDLE || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
181                 Log.e(TAG, "Invalid focus state=" + mState);
182             }
183             mInLongPress = true;
184             // Cancel any outstanding Auto focus requests. The auto focus mode
185             // will be changed from CAF to auto in cancelAutoFocus.
186             onShutterUp();
187             // Call Autofocus
188             onShutterDown();
189             mInLongPress = false;
190         }
191     }
192 
doSnap()193     public void doSnap() {
194         if (!mInitialized) return;
195 
196         // If the user has half-pressed the shutter and focus is completed, we
197         // can take the photo right away. If the focus mode is infinity, we can
198         // also take the photo.
199         if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) {
200             capture();
201         } else if (mState == STATE_FOCUSING) {
202             // Half pressing the shutter (i.e. the focus button event) will
203             // already have requested AF for us, so just request capture on
204             // focus here.
205             mState = STATE_FOCUSING_SNAP_ON_FINISH;
206         } else if (mState == STATE_IDLE) {
207             // We didn't do focus. This can happen if the user press focus key
208             // while the snapshot is still in progress. The user probably wants
209             // the next snapshot as soon as possible, so we just do a snapshot
210             // without focusing again.
211             capture();
212         }
213     }
214 
onShutter()215     public void onShutter() {
216         resetTouchFocus();
217         updateFocusUI();
218     }
219 
onAutoFocus(boolean focused)220     public void onAutoFocus(boolean focused) {
221         if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
222             // Take the picture no matter focus succeeds or fails. No need
223             // to play the AF sound if we're about to play the shutter
224             // sound.
225             if (focused) {
226                 mState = STATE_SUCCESS;
227             } else {
228                 mState = STATE_FAIL;
229             }
230             updateFocusUI();
231             capture();
232         } else if (mState == STATE_FOCUSING) {
233             // This happens when (1) user is half-pressing the focus key or
234             // (2) touch focus is triggered. Play the focus tone. Do not
235             // take the picture now.
236             if (focused) {
237                 mState = STATE_SUCCESS;
238                 // Do not play the sound in continuous autofocus mode. It does
239                 // not do a full scan. The focus callback arrives before doSnap
240                 // so the state is always STATE_FOCUSING.
241                 if (!Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)
242                         && mSoundPlayer != null) {
243                     mSoundPlayer.play();
244                 }
245             } else {
246                 mState = STATE_FAIL;
247             }
248             updateFocusUI();
249             // If this is triggered by touch focus, cancel focus after a
250             // while.
251             if (mFocusArea != null) {
252                 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
253             }
254         } else if (mState == STATE_IDLE) {
255             // User has released the focus key before focus completes.
256             // Do nothing.
257         }
258     }
259 
onTouch(MotionEvent e)260     public boolean onTouch(MotionEvent e) {
261         if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) return false;
262 
263         // Let users be able to cancel previous touch focus.
264         if ((mFocusArea != null) && (mState == STATE_FOCUSING ||
265                     mState == STATE_SUCCESS || mState == STATE_FAIL)) {
266             cancelAutoFocus();
267         }
268 
269         // Initialize variables.
270         int x = Math.round(e.getX());
271         int y = Math.round(e.getY());
272         int focusWidth = mFocusIndicatorRotateLayout.getWidth();
273         int focusHeight = mFocusIndicatorRotateLayout.getHeight();
274         int previewWidth = mPreviewFrame.getWidth();
275         int previewHeight = mPreviewFrame.getHeight();
276         if (mFocusArea == null) {
277             mFocusArea = new ArrayList<Area>();
278             mFocusArea.add(new Area(new Rect(), 1));
279             mMeteringArea = new ArrayList<Area>();
280             mMeteringArea.add(new Area(new Rect(), 1));
281         }
282 
283         // Convert the coordinates to driver format.
284         // AE area is bigger because exposure is sensitive and
285         // easy to over- or underexposure if area is too small.
286         calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight,
287                 mFocusArea.get(0).rect);
288         calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight,
289                 mMeteringArea.get(0).rect);
290 
291         // Use margin to set the focus indicator to the touched area.
292         RelativeLayout.LayoutParams p =
293                 (RelativeLayout.LayoutParams) mFocusIndicatorRotateLayout.getLayoutParams();
294         int left = Util.clamp(x - focusWidth / 2, 0, previewWidth - focusWidth);
295         int top = Util.clamp(y - focusHeight / 2, 0, previewHeight - focusHeight);
296         p.setMargins(left, top, 0, 0);
297         // Disable "center" rule because we no longer want to put it in the center.
298         int[] rules = p.getRules();
299         rules[RelativeLayout.CENTER_IN_PARENT] = 0;
300         mFocusIndicatorRotateLayout.requestLayout();
301 
302         // Stop face detection because we want to specify focus and metering area.
303         mListener.stopFaceDetection();
304 
305         // Set the focus area and metering area.
306         mListener.setFocusParameters();
307         if (mFocusAreaSupported && (e.getAction() == MotionEvent.ACTION_UP)) {
308             autoFocus();
309         } else {  // Just show the indicator in all other cases.
310             updateFocusUI();
311             // Reset the metering area in 3 seconds.
312             mHandler.removeMessages(RESET_TOUCH_FOCUS);
313             mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
314         }
315 
316         return true;
317     }
318 
onPreviewStarted()319     public void onPreviewStarted() {
320         mState = STATE_IDLE;
321     }
322 
onPreviewStopped()323     public void onPreviewStopped() {
324         mState = STATE_IDLE;
325         resetTouchFocus();
326         // If auto focus was in progress, it would have been canceled.
327         updateFocusUI();
328     }
329 
onCameraReleased()330     public void onCameraReleased() {
331         onPreviewStopped();
332     }
333 
autoFocus()334     private void autoFocus() {
335         Log.v(TAG, "Start autofocus.");
336         mListener.autoFocus();
337         mState = STATE_FOCUSING;
338         // Pause the face view because the driver will keep sending face
339         // callbacks after the focus completes.
340         if (mFaceView != null) mFaceView.pause();
341         updateFocusUI();
342         mHandler.removeMessages(RESET_TOUCH_FOCUS);
343     }
344 
cancelAutoFocus()345     private void cancelAutoFocus() {
346         Log.v(TAG, "Cancel autofocus.");
347 
348         // Reset the tap area before calling mListener.cancelAutofocus.
349         // Otherwise, focus mode stays at auto and the tap area passed to the
350         // driver is not reset.
351         resetTouchFocus();
352         mListener.cancelAutoFocus();
353         if (mFaceView != null) mFaceView.resume();
354         mState = STATE_IDLE;
355         updateFocusUI();
356         mHandler.removeMessages(RESET_TOUCH_FOCUS);
357     }
358 
capture()359     private void capture() {
360         if (mListener.capture()) {
361             mState = STATE_IDLE;
362             mHandler.removeMessages(RESET_TOUCH_FOCUS);
363         }
364     }
365 
initializeSoundPlayer(AssetFileDescriptor fd)366     public void initializeSoundPlayer(AssetFileDescriptor fd) {
367         mSoundPlayer = new SoundPlayer(fd);
368     }
369 
releaseSoundPlayer()370     public void releaseSoundPlayer() {
371         if (mSoundPlayer != null) {
372             mSoundPlayer.release();
373             mSoundPlayer = null;
374         }
375     }
376 
377 
378     // This can only be called after mParameters is initialized.
getFocusMode()379     public String getFocusMode() {
380         if (mOverrideFocusMode != null) return mOverrideFocusMode;
381 
382         if (mInLongPress) {
383             // Users long-press the shutter button in CAF. Change it to auto
384             // mode, so it will do a full scan.
385             mFocusMode = Parameters.FOCUS_MODE_AUTO;
386         } else if (mFocusAreaSupported && mFocusArea != null) {
387             // Always use autofocus in tap-to-focus.
388             mFocusMode = Parameters.FOCUS_MODE_AUTO;
389         } else {
390             // The default is continuous autofocus.
391             mFocusMode = mPreferences.getString(
392                     CameraSettings.KEY_FOCUS_MODE, mDefaultFocusMode);
393         }
394         if (!isSupported(mFocusMode, mParameters.getSupportedFocusModes())) {
395             // For some reasons, the driver does not support the current
396             // focus mode. Fall back to auto.
397             if (isSupported(Parameters.FOCUS_MODE_AUTO,
398                     mParameters.getSupportedFocusModes())) {
399                 mFocusMode = Parameters.FOCUS_MODE_AUTO;
400             } else {
401                 mFocusMode = mParameters.getFocusMode();
402             }
403         }
404         return mFocusMode;
405     }
406 
getFocusAreas()407     public List<Area> getFocusAreas() {
408         return mFocusArea;
409     }
410 
getMeteringAreas()411     public List<Area> getMeteringAreas() {
412         return mMeteringArea;
413     }
414 
updateFocusUI()415     public void updateFocusUI() {
416         if (!mInitialized) return;
417 
418         // Set the length of focus indicator according to preview frame size.
419         int len = Math.min(mPreviewFrame.getWidth(), mPreviewFrame.getHeight()) / 4;
420         ViewGroup.LayoutParams layout = mFocusIndicator.getLayoutParams();
421         layout.width = len;
422         layout.height = len;
423 
424         // Show only focus indicator or face indicator.
425         boolean faceExists = (mFaceView != null && mFaceView.faceExists());
426         FocusIndicator focusIndicator = (faceExists) ? mFaceView : mFocusIndicator;
427 
428         if (mState == STATE_IDLE) {
429             if (mFocusArea == null) {
430                 focusIndicator.clear();
431             } else {
432                 // Users touch on the preview and the indicator represents the
433                 // metering area. Either focus area is not supported or
434                 // autoFocus call is not required.
435                 focusIndicator.showStart();
436             }
437         } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
438             focusIndicator.showStart();
439         } else {
440             // In CAF, do not show success or failure because it only returns
441             // the focus status. It does not do a full scan. So the result is
442             // failure most of the time.
443             if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) {
444                 focusIndicator.showStart();
445             } else if (mState == STATE_SUCCESS) {
446                 focusIndicator.showSuccess();
447             } else if (mState == STATE_FAIL) {
448                 focusIndicator.showFail();
449             }
450         }
451     }
452 
resetTouchFocus()453     public void resetTouchFocus() {
454         if (!mInitialized) return;
455 
456         // Put focus indicator to the center.
457         RelativeLayout.LayoutParams p =
458                 (RelativeLayout.LayoutParams) mFocusIndicatorRotateLayout.getLayoutParams();
459         int[] rules = p.getRules();
460         rules[RelativeLayout.CENTER_IN_PARENT] = RelativeLayout.TRUE;
461         p.setMargins(0, 0, 0, 0);
462 
463         mFocusArea = null;
464         mMeteringArea = null;
465     }
466 
calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, int x, int y, int previewWidth, int previewHeight, Rect rect)467     public void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple,
468             int x, int y, int previewWidth, int previewHeight, Rect rect) {
469         int areaWidth = (int)(focusWidth * areaMultiple);
470         int areaHeight = (int)(focusHeight * areaMultiple);
471         int left = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth);
472         int top = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight);
473 
474         RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight);
475         mMatrix.mapRect(rectF);
476         Util.rectFToRect(rectF, rect);
477     }
478 
isFocusCompleted()479     public boolean isFocusCompleted() {
480         return mState == STATE_SUCCESS || mState == STATE_FAIL;
481     }
482 
removeMessages()483     public void removeMessages() {
484         mHandler.removeMessages(RESET_TOUCH_FOCUS);
485     }
486 
overrideFocusMode(String focusMode)487     public void overrideFocusMode(String focusMode) {
488         mOverrideFocusMode = focusMode;
489     }
490 
setAeAwbLock(boolean lock)491     public void setAeAwbLock(boolean lock) {
492         mAeAwbLock = lock;
493     }
494 
getAeAwbLock()495     public boolean getAeAwbLock() {
496         return mAeAwbLock;
497     }
498 
isSupported(String value, List<String> supported)499     private static boolean isSupported(String value, List<String> supported) {
500         return supported == null ? false : supported.indexOf(value) >= 0;
501     }
502 
needAutoFocusCall()503     private boolean needAutoFocusCall() {
504         String focusMode = getFocusMode();
505         return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY)
506                 || focusMode.equals(Parameters.FOCUS_MODE_FIXED)
507                 || focusMode.equals(Parameters.FOCUS_MODE_EDOF));
508     }
509 }
510