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