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.annotation.TargetApi; 20 import android.graphics.Matrix; 21 import android.graphics.Rect; 22 import android.graphics.RectF; 23 import android.hardware.Camera.Area; 24 import android.hardware.Camera.Parameters; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.util.Log; 29 30 import com.android.camera.ui.FaceView; 31 import com.android.camera.ui.FocusIndicator; 32 import com.android.camera.ui.PieRenderer; 33 import com.android.gallery3d.common.ApiHelper; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 38 /* A class that handles everything about focus in still picture mode. 39 * This also handles the metering area because it is the same as focus area. 40 * 41 * The test cases: 42 * (1) The camera has continuous autofocus. Move the camera. Take a picture when 43 * CAF is not in progress. 44 * (2) The camera has continuous autofocus. Move the camera. Take a picture when 45 * CAF is in progress. 46 * (3) The camera has face detection. Point the camera at some faces. Hold the 47 * shutter. Release to take a picture. 48 * (4) The camera has face detection. Point the camera at some faces. Single tap 49 * the shutter to take a picture. 50 * (5) The camera has autofocus. Single tap the shutter to take a picture. 51 * (6) The camera has autofocus. Hold the shutter. Release to take a picture. 52 * (7) The camera has no autofocus. Single tap the shutter and take a picture. 53 * (8) The camera has autofocus and supports focus area. Touch the screen to 54 * trigger autofocus. Take a picture. 55 * (9) The camera has autofocus and supports focus area. Touch the screen to 56 * trigger autofocus. Wait until it times out. 57 * (10) The camera has no autofocus and supports metering area. Touch the screen 58 * to change metering area. 59 */ 60 public class FocusOverlayManager { 61 private static final String TAG = "CAM_FocusManager"; 62 63 private static final int RESET_TOUCH_FOCUS = 0; 64 private static final int RESET_TOUCH_FOCUS_DELAY = 3000; 65 66 private int mState = STATE_IDLE; 67 private static final int STATE_IDLE = 0; // Focus is not active. 68 private static final int STATE_FOCUSING = 1; // Focus is in progress. 69 // Focus is in progress and the camera should take a picture after focus finishes. 70 private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2; 71 private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds. 72 private static final int STATE_FAIL = 4; // Focus finishes and fails. 73 74 private boolean mInitialized; 75 private boolean mFocusAreaSupported; 76 private boolean mMeteringAreaSupported; 77 private boolean mLockAeAwbNeeded; 78 private boolean mAeAwbLock; 79 private Matrix mMatrix; 80 81 private PieRenderer mPieRenderer; 82 83 private int mPreviewWidth; // The width of the preview frame layout. 84 private int mPreviewHeight; // The height of the preview frame layout. 85 private boolean mMirror; // true if the camera is front-facing. 86 private int mDisplayOrientation; 87 private FaceView mFaceView; 88 private List<Object> mFocusArea; // focus area in driver format 89 private List<Object> mMeteringArea; // metering area in driver format 90 private String mFocusMode; 91 private String[] mDefaultFocusModes; 92 private String mOverrideFocusMode; 93 private Parameters mParameters; 94 private ComboPreferences mPreferences; 95 private Handler mHandler; 96 Listener mListener; 97 98 public interface Listener { autoFocus()99 public void autoFocus(); cancelAutoFocus()100 public void cancelAutoFocus(); capture()101 public boolean capture(); startFaceDetection()102 public void startFaceDetection(); stopFaceDetection()103 public void stopFaceDetection(); setFocusParameters()104 public void setFocusParameters(); 105 } 106 107 private class MainHandler extends Handler { MainHandler(Looper looper)108 public MainHandler(Looper looper) { 109 super(looper); 110 } 111 112 @Override handleMessage(Message msg)113 public void handleMessage(Message msg) { 114 switch (msg.what) { 115 case RESET_TOUCH_FOCUS: { 116 cancelAutoFocus(); 117 mListener.startFaceDetection(); 118 break; 119 } 120 } 121 } 122 } 123 FocusOverlayManager(ComboPreferences preferences, String[] defaultFocusModes, Parameters parameters, Listener listener, boolean mirror, Looper looper)124 public FocusOverlayManager(ComboPreferences preferences, String[] defaultFocusModes, 125 Parameters parameters, Listener listener, 126 boolean mirror, Looper looper) { 127 mHandler = new MainHandler(looper); 128 mMatrix = new Matrix(); 129 mPreferences = preferences; 130 mDefaultFocusModes = defaultFocusModes; 131 setParameters(parameters); 132 mListener = listener; 133 setMirror(mirror); 134 } 135 setFocusRenderer(PieRenderer renderer)136 public void setFocusRenderer(PieRenderer renderer) { 137 mPieRenderer = renderer; 138 mInitialized = (mMatrix != null); 139 } 140 setParameters(Parameters parameters)141 public void setParameters(Parameters parameters) { 142 // parameters can only be null when onConfigurationChanged is called 143 // before camera is open. We will just return in this case, because 144 // parameters will be set again later with the right parameters after 145 // camera is open. 146 if (parameters == null) return; 147 mParameters = parameters; 148 mFocusAreaSupported = Util.isFocusAreaSupported(parameters); 149 mMeteringAreaSupported = Util.isMeteringAreaSupported(parameters); 150 mLockAeAwbNeeded = (Util.isAutoExposureLockSupported(mParameters) || 151 Util.isAutoWhiteBalanceLockSupported(mParameters)); 152 } 153 setPreviewSize(int previewWidth, int previewHeight)154 public void setPreviewSize(int previewWidth, int previewHeight) { 155 if (mPreviewWidth != previewWidth || mPreviewHeight != previewHeight) { 156 mPreviewWidth = previewWidth; 157 mPreviewHeight = previewHeight; 158 setMatrix(); 159 } 160 } 161 setMirror(boolean mirror)162 public void setMirror(boolean mirror) { 163 mMirror = mirror; 164 setMatrix(); 165 } 166 setDisplayOrientation(int displayOrientation)167 public void setDisplayOrientation(int displayOrientation) { 168 mDisplayOrientation = displayOrientation; 169 setMatrix(); 170 } 171 setFaceView(FaceView faceView)172 public void setFaceView(FaceView faceView) { 173 mFaceView = faceView; 174 } 175 setMatrix()176 private void setMatrix() { 177 if (mPreviewWidth != 0 && mPreviewHeight != 0) { 178 Matrix matrix = new Matrix(); 179 Util.prepareMatrix(matrix, mMirror, mDisplayOrientation, 180 mPreviewWidth, mPreviewHeight); 181 // In face detection, the matrix converts the driver coordinates to UI 182 // coordinates. In tap focus, the inverted matrix converts the UI 183 // coordinates to driver coordinates. 184 matrix.invert(mMatrix); 185 mInitialized = (mPieRenderer != null); 186 } 187 } 188 lockAeAwbIfNeeded()189 private void lockAeAwbIfNeeded() { 190 if (mLockAeAwbNeeded && !mAeAwbLock) { 191 mAeAwbLock = true; 192 mListener.setFocusParameters(); 193 } 194 } 195 unlockAeAwbIfNeeded()196 private void unlockAeAwbIfNeeded() { 197 if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) { 198 mAeAwbLock = false; 199 mListener.setFocusParameters(); 200 } 201 } 202 onShutterDown()203 public void onShutterDown() { 204 if (!mInitialized) return; 205 206 boolean autoFocusCalled = false; 207 if (needAutoFocusCall()) { 208 // Do not focus if touch focus has been triggered. 209 if (mState != STATE_SUCCESS && mState != STATE_FAIL) { 210 autoFocus(); 211 autoFocusCalled = true; 212 } 213 } 214 215 if (!autoFocusCalled) lockAeAwbIfNeeded(); 216 } 217 onShutterUp()218 public void onShutterUp() { 219 if (!mInitialized) return; 220 221 if (needAutoFocusCall()) { 222 // User releases half-pressed focus key. 223 if (mState == STATE_FOCUSING || mState == STATE_SUCCESS 224 || mState == STATE_FAIL) { 225 cancelAutoFocus(); 226 } 227 } 228 229 // Unlock AE and AWB after cancelAutoFocus. Camera API does not 230 // guarantee setParameters can be called during autofocus. 231 unlockAeAwbIfNeeded(); 232 } 233 doSnap()234 public void doSnap() { 235 if (!mInitialized) return; 236 237 // If the user has half-pressed the shutter and focus is completed, we 238 // can take the photo right away. If the focus mode is infinity, we can 239 // also take the photo. 240 if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) { 241 capture(); 242 } else if (mState == STATE_FOCUSING) { 243 // Half pressing the shutter (i.e. the focus button event) will 244 // already have requested AF for us, so just request capture on 245 // focus here. 246 mState = STATE_FOCUSING_SNAP_ON_FINISH; 247 } else if (mState == STATE_IDLE) { 248 // We didn't do focus. This can happen if the user press focus key 249 // while the snapshot is still in progress. The user probably wants 250 // the next snapshot as soon as possible, so we just do a snapshot 251 // without focusing again. 252 capture(); 253 } 254 } 255 onAutoFocus(boolean focused, boolean shutterButtonPressed)256 public void onAutoFocus(boolean focused, boolean shutterButtonPressed) { 257 if (mState == STATE_FOCUSING_SNAP_ON_FINISH) { 258 // Take the picture no matter focus succeeds or fails. No need 259 // to play the AF sound if we're about to play the shutter 260 // sound. 261 if (focused) { 262 mState = STATE_SUCCESS; 263 } else { 264 mState = STATE_FAIL; 265 } 266 updateFocusUI(); 267 capture(); 268 } else if (mState == STATE_FOCUSING) { 269 // This happens when (1) user is half-pressing the focus key or 270 // (2) touch focus is triggered. Play the focus tone. Do not 271 // take the picture now. 272 if (focused) { 273 mState = STATE_SUCCESS; 274 } else { 275 mState = STATE_FAIL; 276 } 277 updateFocusUI(); 278 // If this is triggered by touch focus, cancel focus after a 279 // while. 280 if (mFocusArea != null) { 281 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); 282 } 283 if (shutterButtonPressed) { 284 // Lock AE & AWB so users can half-press shutter and recompose. 285 lockAeAwbIfNeeded(); 286 } 287 } else if (mState == STATE_IDLE) { 288 // User has released the focus key before focus completes. 289 // Do nothing. 290 } 291 } 292 onAutoFocusMoving(boolean moving)293 public void onAutoFocusMoving(boolean moving) { 294 if (!mInitialized) return; 295 // Ignore if the camera has detected some faces. 296 if (mFaceView != null && mFaceView.faceExists()) { 297 mPieRenderer.clear(); 298 return; 299 } 300 301 // Ignore if we have requested autofocus. This method only handles 302 // continuous autofocus. 303 if (mState != STATE_IDLE) return; 304 305 if (moving) { 306 mPieRenderer.showStart(); 307 } else { 308 mPieRenderer.showSuccess(true); 309 } 310 } 311 312 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) initializeFocusAreas(int focusWidth, int focusHeight, int x, int y, int previewWidth, int previewHeight)313 private void initializeFocusAreas(int focusWidth, int focusHeight, 314 int x, int y, int previewWidth, int previewHeight) { 315 if (mFocusArea == null) { 316 mFocusArea = new ArrayList<Object>(); 317 mFocusArea.add(new Area(new Rect(), 1)); 318 } 319 320 // Convert the coordinates to driver format. 321 calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight, 322 ((Area) mFocusArea.get(0)).rect); 323 } 324 325 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) initializeMeteringAreas(int focusWidth, int focusHeight, int x, int y, int previewWidth, int previewHeight)326 private void initializeMeteringAreas(int focusWidth, int focusHeight, 327 int x, int y, int previewWidth, int previewHeight) { 328 if (mMeteringArea == null) { 329 mMeteringArea = new ArrayList<Object>(); 330 mMeteringArea.add(new Area(new Rect(), 1)); 331 } 332 333 // Convert the coordinates to driver format. 334 // AE area is bigger because exposure is sensitive and 335 // easy to over- or underexposure if area is too small. 336 calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight, 337 ((Area) mMeteringArea.get(0)).rect); 338 } 339 onSingleTapUp(int x, int y)340 public void onSingleTapUp(int x, int y) { 341 if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) return; 342 343 // Let users be able to cancel previous touch focus. 344 if ((mFocusArea != null) && (mState == STATE_FOCUSING || 345 mState == STATE_SUCCESS || mState == STATE_FAIL)) { 346 cancelAutoFocus(); 347 } 348 // Initialize variables. 349 int focusWidth = mPieRenderer.getSize(); 350 int focusHeight = mPieRenderer.getSize(); 351 if (focusWidth == 0 || mPieRenderer.getWidth() == 0 352 || mPieRenderer.getHeight() == 0) return; 353 int previewWidth = mPreviewWidth; 354 int previewHeight = mPreviewHeight; 355 // Initialize mFocusArea. 356 if (mFocusAreaSupported) { 357 initializeFocusAreas( 358 focusWidth, focusHeight, x, y, previewWidth, previewHeight); 359 } 360 // Initialize mMeteringArea. 361 if (mMeteringAreaSupported) { 362 initializeMeteringAreas( 363 focusWidth, focusHeight, x, y, previewWidth, previewHeight); 364 } 365 366 // Use margin to set the focus indicator to the touched area. 367 mPieRenderer.setFocus(x, y); 368 369 // Stop face detection because we want to specify focus and metering area. 370 mListener.stopFaceDetection(); 371 372 // Set the focus area and metering area. 373 mListener.setFocusParameters(); 374 if (mFocusAreaSupported) { 375 autoFocus(); 376 } else { // Just show the indicator in all other cases. 377 updateFocusUI(); 378 // Reset the metering area in 3 seconds. 379 mHandler.removeMessages(RESET_TOUCH_FOCUS); 380 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); 381 } 382 } 383 onPreviewStarted()384 public void onPreviewStarted() { 385 mState = STATE_IDLE; 386 } 387 onPreviewStopped()388 public void onPreviewStopped() { 389 // If auto focus was in progress, it would have been stopped. 390 mState = STATE_IDLE; 391 resetTouchFocus(); 392 updateFocusUI(); 393 } 394 onCameraReleased()395 public void onCameraReleased() { 396 onPreviewStopped(); 397 } 398 autoFocus()399 private void autoFocus() { 400 Log.v(TAG, "Start autofocus."); 401 mListener.autoFocus(); 402 mState = STATE_FOCUSING; 403 // Pause the face view because the driver will keep sending face 404 // callbacks after the focus completes. 405 if (mFaceView != null) mFaceView.pause(); 406 updateFocusUI(); 407 mHandler.removeMessages(RESET_TOUCH_FOCUS); 408 } 409 cancelAutoFocus()410 private void cancelAutoFocus() { 411 Log.v(TAG, "Cancel autofocus."); 412 413 // Reset the tap area before calling mListener.cancelAutofocus. 414 // Otherwise, focus mode stays at auto and the tap area passed to the 415 // driver is not reset. 416 resetTouchFocus(); 417 mListener.cancelAutoFocus(); 418 if (mFaceView != null) mFaceView.resume(); 419 mState = STATE_IDLE; 420 updateFocusUI(); 421 mHandler.removeMessages(RESET_TOUCH_FOCUS); 422 } 423 capture()424 private void capture() { 425 if (mListener.capture()) { 426 mState = STATE_IDLE; 427 mHandler.removeMessages(RESET_TOUCH_FOCUS); 428 } 429 } 430 getFocusMode()431 public String getFocusMode() { 432 if (mOverrideFocusMode != null) return mOverrideFocusMode; 433 List<String> supportedFocusModes = mParameters.getSupportedFocusModes(); 434 435 if (mFocusAreaSupported && mFocusArea != null) { 436 // Always use autofocus in tap-to-focus. 437 mFocusMode = Parameters.FOCUS_MODE_AUTO; 438 } else { 439 // The default is continuous autofocus. 440 mFocusMode = mPreferences.getString( 441 CameraSettings.KEY_FOCUS_MODE, null); 442 443 // Try to find a supported focus mode from the default list. 444 if (mFocusMode == null) { 445 for (int i = 0; i < mDefaultFocusModes.length; i++) { 446 String mode = mDefaultFocusModes[i]; 447 if (Util.isSupported(mode, supportedFocusModes)) { 448 mFocusMode = mode; 449 break; 450 } 451 } 452 } 453 } 454 if (!Util.isSupported(mFocusMode, supportedFocusModes)) { 455 // For some reasons, the driver does not support the current 456 // focus mode. Fall back to auto. 457 if (Util.isSupported(Parameters.FOCUS_MODE_AUTO, 458 mParameters.getSupportedFocusModes())) { 459 mFocusMode = Parameters.FOCUS_MODE_AUTO; 460 } else { 461 mFocusMode = mParameters.getFocusMode(); 462 } 463 } 464 return mFocusMode; 465 } 466 getFocusAreas()467 public List getFocusAreas() { 468 return mFocusArea; 469 } 470 getMeteringAreas()471 public List getMeteringAreas() { 472 return mMeteringArea; 473 } 474 updateFocusUI()475 public void updateFocusUI() { 476 if (!mInitialized) return; 477 // Show only focus indicator or face indicator. 478 boolean faceExists = (mFaceView != null && mFaceView.faceExists()); 479 FocusIndicator focusIndicator = (faceExists) ? mFaceView : mPieRenderer; 480 481 if (mState == STATE_IDLE) { 482 if (mFocusArea == null) { 483 focusIndicator.clear(); 484 } else { 485 // Users touch on the preview and the indicator represents the 486 // metering area. Either focus area is not supported or 487 // autoFocus call is not required. 488 focusIndicator.showStart(); 489 } 490 } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) { 491 focusIndicator.showStart(); 492 } else { 493 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) { 494 // TODO: check HAL behavior and decide if this can be removed. 495 focusIndicator.showSuccess(false); 496 } else if (mState == STATE_SUCCESS) { 497 focusIndicator.showSuccess(false); 498 } else if (mState == STATE_FAIL) { 499 focusIndicator.showFail(false); 500 } 501 } 502 } 503 resetTouchFocus()504 public void resetTouchFocus() { 505 if (!mInitialized) return; 506 507 // Put focus indicator to the center. clear reset position 508 mPieRenderer.clear(); 509 510 mFocusArea = null; 511 mMeteringArea = null; 512 } 513 calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, int x, int y, int previewWidth, int previewHeight, Rect rect)514 private void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, 515 int x, int y, int previewWidth, int previewHeight, Rect rect) { 516 int areaWidth = (int) (focusWidth * areaMultiple); 517 int areaHeight = (int) (focusHeight * areaMultiple); 518 int left = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth); 519 int top = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight); 520 521 RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight); 522 mMatrix.mapRect(rectF); 523 Util.rectFToRect(rectF, rect); 524 } 525 getFocusState()526 /* package */ int getFocusState() { 527 return mState; 528 } 529 isFocusCompleted()530 public boolean isFocusCompleted() { 531 return mState == STATE_SUCCESS || mState == STATE_FAIL; 532 } 533 isFocusingSnapOnFinish()534 public boolean isFocusingSnapOnFinish() { 535 return mState == STATE_FOCUSING_SNAP_ON_FINISH; 536 } 537 removeMessages()538 public void removeMessages() { 539 mHandler.removeMessages(RESET_TOUCH_FOCUS); 540 } 541 overrideFocusMode(String focusMode)542 public void overrideFocusMode(String focusMode) { 543 mOverrideFocusMode = focusMode; 544 } 545 setAeAwbLock(boolean lock)546 public void setAeAwbLock(boolean lock) { 547 mAeAwbLock = lock; 548 } 549 getAeAwbLock()550 public boolean getAeAwbLock() { 551 return mAeAwbLock; 552 } 553 needAutoFocusCall()554 private boolean needAutoFocusCall() { 555 String focusMode = getFocusMode(); 556 return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY) 557 || focusMode.equals(Parameters.FOCUS_MODE_FIXED) 558 || focusMode.equals(Parameters.FOCUS_MODE_EDOF)); 559 } 560 } 561