• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 android.graphics.Rect;
20 import android.graphics.RectF;
21 import android.hardware.Camera.Area;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 
26 import com.android.camera.app.AppController;
27 import com.android.camera.app.MotionManager;
28 import com.android.camera.debug.Log;
29 import com.android.camera.one.Settings3A;
30 import com.android.camera.settings.Keys;
31 import com.android.camera.settings.SettingsManager;
32 import com.android.camera.ui.PreviewStatusListener;
33 import com.android.camera.ui.TouchCoordinate;
34 import com.android.camera.util.ApiHelper;
35 import com.android.camera.ui.focus.CameraCoordinateTransformer;
36 import com.android.camera.ui.focus.FocusRing;
37 import com.android.camera.util.CameraUtil;
38 import com.android.camera.stats.UsageStatistics;
39 import com.android.ex.camera2.portability.CameraCapabilities;
40 
41 import java.lang.ref.WeakReference;
42 import java.util.ArrayList;
43 import java.util.List;
44 
45 /* A class that handles everything about focus in still picture mode.
46  * This also handles the metering area because it is the same as focus area.
47  *
48  * The test cases:
49  * (1) The camera has continuous autofocus. Move the camera. Take a picture when
50  *     CAF is not in progress.
51  * (2) The camera has continuous autofocus. Move the camera. Take a picture when
52  *     CAF is in progress.
53  * (3) The camera has face detection. Point the camera at some faces. Hold the
54  *     shutter. Release to take a picture.
55  * (4) The camera has face detection. Point the camera at some faces. Single tap
56  *     the shutter to take a picture.
57  * (5) The camera has autofocus. Single tap the shutter to take a picture.
58  * (6) The camera has autofocus. Hold the shutter. Release to take a picture.
59  * (7) The camera has no autofocus. Single tap the shutter and take a picture.
60  * (8) The camera has autofocus and supports focus area. Touch the screen to
61  *     trigger autofocus. Take a picture.
62  * (9) The camera has autofocus and supports focus area. Touch the screen to
63  *     trigger autofocus. Wait until it times out.
64  * (10) The camera has no autofocus and supports metering area. Touch the screen
65  *     to change metering area.
66  */
67 public class FocusOverlayManager implements PreviewStatusListener.PreviewAreaChangedListener,
68         MotionManager.MotionListener {
69     private static final Log.Tag TAG = new Log.Tag("FocusOverlayMgr");
70 
71     private static final int RESET_TOUCH_FOCUS = 0;
72 
73     private static final int RESET_TOUCH_FOCUS_DELAY_MILLIS = Settings3A.getFocusHoldMillis();
74 
75     public static final float AF_REGION_BOX = Settings3A.getAutoFocusRegionWidth();
76     public static final float AE_REGION_BOX = Settings3A.getMeteringRegionWidth();
77 
78     private int mState = STATE_IDLE;
79     private static final int STATE_IDLE = 0; // Focus is not active.
80     private static final int STATE_FOCUSING = 1; // Focus is in progress.
81     // Focus is in progress and the camera should take a picture after focus finishes.
82     private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2;
83     private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds.
84     private static final int STATE_FAIL = 4; // Focus finishes and fails.
85 
86     private boolean mInitialized;
87     private boolean mFocusAreaSupported;
88     private boolean mMeteringAreaSupported;
89     private boolean mLockAeAwbNeeded;
90     private boolean mAeAwbLock;
91     private CameraCoordinateTransformer mCoordinateTransformer;
92 
93     private boolean mMirror; // true if the camera is front-facing.
94     private int mDisplayOrientation;
95     private List<Area> mFocusArea; // focus area in driver format
96     private List<Area> mMeteringArea; // metering area in driver format
97     private CameraCapabilities.FocusMode mFocusMode;
98     private final List<CameraCapabilities.FocusMode> mDefaultFocusModes;
99     private CameraCapabilities.FocusMode mOverrideFocusMode;
100     private CameraCapabilities mCapabilities;
101     private final AppController mAppController;
102     private final SettingsManager mSettingsManager;
103     private final Handler mHandler;
104     Listener mListener;
105     private boolean mPreviousMoving;
106     private final FocusRing mFocusRing;
107     private final Rect mPreviewRect = new Rect(0, 0, 0, 0);
108     private boolean mFocusLocked;
109 
110     /** Manual tap to focus parameters */
111     private TouchCoordinate mTouchCoordinate;
112     private long mTouchTime;
113 
114     public interface Listener {
autoFocus()115         public void autoFocus();
cancelAutoFocus()116         public void cancelAutoFocus();
capture()117         public boolean capture();
startFaceDetection()118         public void startFaceDetection();
stopFaceDetection()119         public void stopFaceDetection();
setFocusParameters()120         public void setFocusParameters();
121     }
122 
123     /**
124      * TODO: Refactor this so that we either don't need a handler or make
125      * mListener not be the activity.
126      */
127     private static class MainHandler extends Handler {
128         /**
129          * The outer mListener at the moment is actually the CameraActivity,
130          * which we would leak if we didn't break the GC path here using a
131          * WeakReference.
132          */
133         final WeakReference<FocusOverlayManager> mManager;
MainHandler(FocusOverlayManager manager, Looper looper)134         public MainHandler(FocusOverlayManager manager, Looper looper) {
135             super(looper);
136             mManager = new WeakReference<FocusOverlayManager>(manager);
137         }
138 
139         @Override
handleMessage(Message msg)140         public void handleMessage(Message msg) {
141             FocusOverlayManager manager = mManager.get();
142             if (manager == null) {
143                 return;
144             }
145 
146             switch (msg.what) {
147                 case RESET_TOUCH_FOCUS: {
148                     manager.cancelAutoFocus();
149                     manager.mListener.startFaceDetection();
150                     break;
151                 }
152             }
153         }
154     }
155 
FocusOverlayManager(AppController appController, List<CameraCapabilities.FocusMode> defaultFocusModes, CameraCapabilities capabilities, Listener listener, boolean mirror, Looper looper, FocusRing focusRing)156     public FocusOverlayManager(AppController appController,
157             List<CameraCapabilities.FocusMode> defaultFocusModes, CameraCapabilities capabilities,
158             Listener listener, boolean mirror, Looper looper, FocusRing focusRing) {
159         mAppController = appController;
160         mSettingsManager = appController.getSettingsManager();
161         mHandler = new MainHandler(this, looper);
162         mDefaultFocusModes = new ArrayList<CameraCapabilities.FocusMode>(defaultFocusModes);
163         updateCapabilities(capabilities);
164         mListener = listener;
165         setMirror(mirror);
166         mFocusRing = focusRing;
167         mFocusLocked = false;
168     }
169 
updateCapabilities(CameraCapabilities capabilities)170     public void updateCapabilities(CameraCapabilities capabilities) {
171         // capabilities can only be null when onConfigurationChanged is called
172         // before camera is open. We will just return in this case, because
173         // capabilities will be set again later with the right capabilities after
174         // camera is open.
175         if (capabilities == null) {
176             return;
177         }
178         mCapabilities = capabilities;
179         mFocusAreaSupported = mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
180         mMeteringAreaSupported = mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
181         mLockAeAwbNeeded = (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)
182                 || mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK));
183     }
184 
185     /** This setter should be the only way to mutate mPreviewRect. */
setPreviewRect(Rect previewRect)186     public void setPreviewRect(Rect previewRect) {
187         if (!mPreviewRect.equals(previewRect)) {
188             mPreviewRect.set(previewRect);
189             mFocusRing.configurePreviewDimensions(CameraUtil.rectToRectF(mPreviewRect));
190             resetCoordinateTransformer();
191             mInitialized = true;
192         }
193     }
194 
195     @Override
onPreviewAreaChanged(RectF previewArea)196     public void onPreviewAreaChanged(RectF previewArea) {
197         setPreviewRect(CameraUtil.rectFToRect(previewArea));
198     }
199 
setMirror(boolean mirror)200     public void setMirror(boolean mirror) {
201         mMirror = mirror;
202         resetCoordinateTransformer();
203     }
204 
setDisplayOrientation(int displayOrientation)205     public void setDisplayOrientation(int displayOrientation) {
206         mDisplayOrientation = displayOrientation;
207         resetCoordinateTransformer();
208     }
209 
resetCoordinateTransformer()210     private void resetCoordinateTransformer() {
211         if (mPreviewRect.width() > 0 && mPreviewRect.height() > 0) {
212             mCoordinateTransformer = new CameraCoordinateTransformer(mMirror, mDisplayOrientation,
213                   CameraUtil.rectToRectF(mPreviewRect));
214         } else {
215             Log.w(TAG, "The coordinate transformer could not be built because the preview rect"
216                   + "did not have a width and height");
217         }
218     }
219 
220 
lockAeAwbIfNeeded()221     private void lockAeAwbIfNeeded() {
222         if (mLockAeAwbNeeded && !mAeAwbLock) {
223             mAeAwbLock = true;
224             mListener.setFocusParameters();
225         }
226     }
227 
unlockAeAwbIfNeeded()228     private void unlockAeAwbIfNeeded() {
229         if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) {
230             mAeAwbLock = false;
231             mListener.setFocusParameters();
232         }
233     }
234 
onShutterUp(CameraCapabilities.FocusMode currentFocusMode)235     public void onShutterUp(CameraCapabilities.FocusMode currentFocusMode) {
236         if (!mInitialized) {
237             return;
238         }
239 
240         if (needAutoFocusCall(currentFocusMode)) {
241             // User releases half-pressed focus key.
242             if (mState == STATE_FOCUSING || mState == STATE_SUCCESS
243                     || mState == STATE_FAIL) {
244                 cancelAutoFocus();
245             }
246         }
247 
248         // Unlock AE and AWB after cancelAutoFocus. Camera API does not
249         // guarantee setParameters can be called during autofocus.
250         unlockAeAwbIfNeeded();
251     }
252 
focusAndCapture(CameraCapabilities.FocusMode currentFocusMode)253     public void focusAndCapture(CameraCapabilities.FocusMode currentFocusMode) {
254         if (!mInitialized) {
255             return;
256         }
257 
258         if (!needAutoFocusCall(currentFocusMode)) {
259             // Focus is not needed.
260             capture();
261         } else if (mState == STATE_SUCCESS || mState == STATE_FAIL) {
262             // Focus is done already.
263             capture();
264         } else if (mState == STATE_FOCUSING) {
265             // Still focusing and will not trigger snap upon finish.
266             mState = STATE_FOCUSING_SNAP_ON_FINISH;
267         } else if (mState == STATE_IDLE) {
268             autoFocusAndCapture();
269         }
270     }
271 
onAutoFocus(boolean focused, boolean shutterButtonPressed)272     public void onAutoFocus(boolean focused, boolean shutterButtonPressed) {
273         if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
274             // Take the picture no matter focus succeeds or fails. No need
275             // to play the AF sound if we're about to play the shutter
276             // sound.
277             if (focused) {
278                 mState = STATE_SUCCESS;
279             } else {
280                 mState = STATE_FAIL;
281             }
282             capture();
283         } else if (mState == STATE_FOCUSING) {
284             // This happens when (1) user is half-pressing the focus key or
285             // (2) touch focus is triggered. Play the focus tone. Do not
286             // take the picture now.
287             if (focused) {
288                 mState = STATE_SUCCESS;
289             } else {
290                 mState = STATE_FAIL;
291             }
292             // If this is triggered by touch focus, cancel focus after a
293             // while.
294             if (mFocusArea != null) {
295                 mFocusLocked = true;
296                 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY_MILLIS);
297             }
298             if (shutterButtonPressed) {
299                 // Lock AE & AWB so users can half-press shutter and recompose.
300                 lockAeAwbIfNeeded();
301             }
302         } else if (mState == STATE_IDLE) {
303             // User has released the focus key before focus completes.
304             // Do nothing.
305         }
306     }
307 
onAutoFocusMoving(boolean moving)308     public void onAutoFocusMoving(boolean moving) {
309         if (!mInitialized) {
310             return;
311         }
312 
313         // Ignore if we have requested autofocus. This method only handles
314         // continuous autofocus.
315         if (mState != STATE_IDLE) {
316             return;
317         }
318 
319         // animate on false->true trasition only b/8219520
320         if (moving && !mPreviousMoving) {
321             // Auto focus at the center of the preview.
322             mFocusRing.startPassiveFocus();
323             mFocusRing.centerFocusLocation();
324         } else if (!moving && mFocusRing.isPassiveFocusRunning()) {
325             mFocusRing.stopFocusAnimations();
326         }
327         mPreviousMoving = moving;
328     }
329 
330     /** Returns width of auto focus region in pixels. */
getAFRegionSizePx()331     private int getAFRegionSizePx() {
332         return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AF_REGION_BOX);
333     }
334 
335     /** Returns width of metering region in pixels. */
getAERegionSizePx()336     private int getAERegionSizePx() {
337         return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AE_REGION_BOX);
338     }
339 
initializeFocusAreas(int x, int y)340     private void initializeFocusAreas(int x, int y) {
341         if (mFocusArea == null) {
342             mFocusArea = new ArrayList<Area>();
343             mFocusArea.add(new Area(new Rect(), 1));
344         }
345 
346         // Convert the coordinates to driver format.
347         mFocusArea.get(0).rect = computeCameraRectFromPreviewCoordinates(x, y, getAFRegionSizePx());
348     }
349 
initializeMeteringAreas(int x, int y)350     private void initializeMeteringAreas(int x, int y) {
351         if (mMeteringArea == null) {
352             mMeteringArea = new ArrayList<Area>();
353             mMeteringArea.add(new Area(new Rect(), 1));
354         }
355 
356         // Convert the coordinates to driver format.
357         mMeteringArea.get(0).rect = computeCameraRectFromPreviewCoordinates(x, y, getAERegionSizePx());
358     }
359 
onSingleTapUp(int x, int y)360     public void onSingleTapUp(int x, int y) {
361         if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
362             return;
363         }
364 
365         // Let users be able to cancel previous touch focus.
366         if ((mFocusArea != null) && (mState == STATE_FOCUSING ||
367                     mState == STATE_SUCCESS || mState == STATE_FAIL)) {
368             cancelAutoFocus();
369         }
370         if (mPreviewRect.width() == 0 || mPreviewRect.height() == 0) {
371             return;
372         }
373         // Initialize variables.
374         // Initialize mFocusArea.
375         if (mFocusAreaSupported) {
376             initializeFocusAreas(x, y);
377         }
378         // Initialize mMeteringArea.
379         if (mMeteringAreaSupported) {
380             initializeMeteringAreas(x, y);
381         }
382 
383         mFocusRing.startActiveFocus();
384         mFocusRing.setFocusLocation(x, y);
385 
386         // Log manual tap to focus.
387         mTouchCoordinate = new TouchCoordinate(x, y, mPreviewRect.width(), mPreviewRect.height());
388         mTouchTime = System.currentTimeMillis();
389 
390         // Stop face detection because we want to specify focus and metering area.
391         mListener.stopFaceDetection();
392 
393         // Set the focus area and metering area.
394         mListener.setFocusParameters();
395         if (mFocusAreaSupported) {
396             autoFocus();
397         } else {  // Just show the indicator in all other cases.
398             // Reset the metering area in 4 seconds.
399             mHandler.removeMessages(RESET_TOUCH_FOCUS);
400             mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY_MILLIS);
401         }
402     }
403 
onPreviewStarted()404     public void onPreviewStarted() {
405         mState = STATE_IDLE;
406         // Avoid resetting touch focus if N4, b/18681082.
407         if (!ApiHelper.IS_NEXUS_4) {
408             resetTouchFocus();
409         }
410     }
411 
onPreviewStopped()412     public void onPreviewStopped() {
413         // If auto focus was in progress, it would have been stopped.
414         mState = STATE_IDLE;
415     }
416 
onCameraReleased()417     public void onCameraReleased() {
418         onPreviewStopped();
419     }
420 
421     @Override
onMoving()422     public void onMoving() {
423         if (mFocusLocked) {
424             Log.d(TAG, "onMoving: Early focus unlock.");
425             cancelAutoFocus();
426         }
427     }
428 
429     /**
430      * Triggers the autofocus and sets the specified state.
431      *
432      * @param focusingState The state to use when focus is in progress.
433      */
autoFocus(int focusingState)434     private void autoFocus(int focusingState) {
435         mListener.autoFocus();
436         mState = focusingState;
437         mHandler.removeMessages(RESET_TOUCH_FOCUS);
438     }
439 
440     /**
441      * Triggers the autofocus and set the state to indicate the focus is in
442      * progress.
443      */
autoFocus()444     private void autoFocus() {
445         autoFocus(STATE_FOCUSING);
446     }
447 
448     /**
449      * Triggers the autofocus and set the state to which a capture will happen
450      * in the following autofocus callback.
451      */
autoFocusAndCapture()452     private void autoFocusAndCapture() {
453         autoFocus(STATE_FOCUSING_SNAP_ON_FINISH);
454     }
455 
cancelAutoFocus()456     private void cancelAutoFocus() {
457         Log.v(TAG, "Cancel autofocus.");
458         // Reset the tap area before calling mListener.cancelAutofocus.
459         // Otherwise, focus mode stays at auto and the tap area passed to the
460         // driver is not reset.
461         resetTouchFocus();
462         mListener.cancelAutoFocus();
463         mState = STATE_IDLE;
464         mFocusLocked = false;
465         mHandler.removeMessages(RESET_TOUCH_FOCUS);
466     }
467 
capture()468     private void capture() {
469         if (mListener.capture()) {
470             mState = STATE_IDLE;
471             mHandler.removeMessages(RESET_TOUCH_FOCUS);
472         }
473     }
474 
getFocusMode( final CameraCapabilities.FocusMode currentFocusMode)475     public CameraCapabilities.FocusMode getFocusMode(
476             final CameraCapabilities.FocusMode currentFocusMode) {
477         if (mOverrideFocusMode != null) {
478             Log.v(TAG, "returning override focus: " + mOverrideFocusMode);
479             return mOverrideFocusMode;
480         }
481         if (mCapabilities == null) {
482             Log.v(TAG, "no capabilities, returning default AUTO focus mode");
483             return CameraCapabilities.FocusMode.AUTO;
484         }
485 
486         if (mFocusAreaSupported && mFocusArea != null) {
487             Log.v(TAG, "in tap to focus, returning AUTO focus mode");
488             // Always use autofocus in tap-to-focus.
489             mFocusMode = CameraCapabilities.FocusMode.AUTO;
490         } else {
491             String focusSetting = mSettingsManager.getString(mAppController.getCameraScope(),
492                     Keys.KEY_FOCUS_MODE);
493             Log.v(TAG, "stored focus setting for camera: " + focusSetting);
494             // The default is continuous autofocus.
495             mFocusMode = mCapabilities.getStringifier().focusModeFromString(focusSetting);
496             Log.v(TAG, "focus mode resolved from setting: " + mFocusMode);
497             // Try to find a supported focus mode from the default list.
498             if (mFocusMode == null) {
499                 for (CameraCapabilities.FocusMode mode : mDefaultFocusModes) {
500                     if (mCapabilities.supports(mode)) {
501                         mFocusMode = mode;
502                         Log.v(TAG, "selected supported focus mode from default list" + mode);
503                         break;
504                     }
505                 }
506             }
507         }
508         if (!mCapabilities.supports(mFocusMode)) {
509             // For some reasons, the driver does not support the current
510             // focus mode. Fall back to auto.
511             if (mCapabilities.supports(CameraCapabilities.FocusMode.AUTO)) {
512                 Log.v(TAG, "no supported focus mode, falling back to AUTO");
513                 mFocusMode = CameraCapabilities.FocusMode.AUTO;
514             } else {
515                 Log.v(TAG, "no supported focus mode, falling back to current: " + currentFocusMode);
516                 mFocusMode = currentFocusMode;
517             }
518         }
519         return mFocusMode;
520     }
521 
getFocusAreas()522     public List<Area> getFocusAreas() {
523         return mFocusArea;
524     }
525 
getMeteringAreas()526     public List<Area> getMeteringAreas() {
527         return mMeteringArea;
528     }
529 
resetTouchFocus()530     public void resetTouchFocus() {
531         if (!mInitialized) {
532             return;
533         }
534 
535         mFocusArea = null;
536         mMeteringArea = null;
537         // This will cause current module to call getFocusAreas() and
538         // getMeteringAreas() and send updated regions to camera.
539         mListener.setFocusParameters();
540 
541         if (mTouchCoordinate != null) {
542             UsageStatistics.instance().tapToFocus(mTouchCoordinate,
543                     0.001f * (System.currentTimeMillis() - mTouchTime));
544             mTouchCoordinate = null;
545         }
546     }
547 
computeCameraRectFromPreviewCoordinates(int x, int y, int size)548     private Rect computeCameraRectFromPreviewCoordinates(int x, int y, int size) {
549         int left = CameraUtil.clamp(x - size / 2, mPreviewRect.left,
550                 mPreviewRect.right - size);
551         int top = CameraUtil.clamp(y - size / 2, mPreviewRect.top,
552                 mPreviewRect.bottom - size);
553 
554         RectF rectF = new RectF(left, top, left + size, top + size);
555         return CameraUtil.rectFToRect(mCoordinateTransformer.toCameraSpace(rectF));
556     }
557 
getFocusState()558     /* package */ int getFocusState() {
559         return mState;
560     }
561 
isFocusCompleted()562     public boolean isFocusCompleted() {
563         return mState == STATE_SUCCESS || mState == STATE_FAIL;
564     }
565 
isFocusingSnapOnFinish()566     public boolean isFocusingSnapOnFinish() {
567         return mState == STATE_FOCUSING_SNAP_ON_FINISH;
568     }
569 
removeMessages()570     public void removeMessages() {
571         mHandler.removeMessages(RESET_TOUCH_FOCUS);
572     }
573 
overrideFocusMode(CameraCapabilities.FocusMode focusMode)574     public void overrideFocusMode(CameraCapabilities.FocusMode focusMode) {
575         mOverrideFocusMode = focusMode;
576     }
577 
setAeAwbLock(boolean lock)578     public void setAeAwbLock(boolean lock) {
579         mAeAwbLock = lock;
580     }
581 
getAeAwbLock()582     public boolean getAeAwbLock() {
583         return mAeAwbLock;
584     }
585 
needAutoFocusCall(CameraCapabilities.FocusMode focusMode)586     private boolean needAutoFocusCall(CameraCapabilities.FocusMode focusMode) {
587         return !(focusMode == CameraCapabilities.FocusMode.INFINITY
588                 || focusMode == CameraCapabilities.FocusMode.FIXED
589                 || focusMode == CameraCapabilities.FocusMode.EXTENDED_DOF);
590     }
591 }
592