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 { 115 public void autoFocus(); 116 public void cancelAutoFocus(); 117 public boolean capture(); 118 public void startFaceDetection(); 119 public void stopFaceDetection(); 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; 134 public MainHandler(FocusOverlayManager manager, Looper looper) { 135 super(looper); 136 mManager = new WeakReference<FocusOverlayManager>(manager); 137 } 138 139 @Override 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 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 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. */ 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 196 public void onPreviewAreaChanged(RectF previewArea) { 197 setPreviewRect(CameraUtil.rectFToRect(previewArea)); 198 } 199 200 public void setMirror(boolean mirror) { 201 mMirror = mirror; 202 resetCoordinateTransformer(); 203 } 204 205 public void setDisplayOrientation(int displayOrientation) { 206 mDisplayOrientation = displayOrientation; 207 resetCoordinateTransformer(); 208 } 209 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 221 private void lockAeAwbIfNeeded() { 222 if (mLockAeAwbNeeded && !mAeAwbLock) { 223 mAeAwbLock = true; 224 mListener.setFocusParameters(); 225 } 226 } 227 228 private void unlockAeAwbIfNeeded() { 229 if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) { 230 mAeAwbLock = false; 231 mListener.setFocusParameters(); 232 } 233 } 234 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 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 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 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. */ 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. */ 336 private int getAERegionSizePx() { 337 return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AE_REGION_BOX); 338 } 339 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 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 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 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 412 public void onPreviewStopped() { 413 // If auto focus was in progress, it would have been stopped. 414 mState = STATE_IDLE; 415 } 416 417 public void onCameraReleased() { 418 onPreviewStopped(); 419 } 420 421 @Override 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 */ 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 */ 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 */ 452 private void autoFocusAndCapture() { 453 autoFocus(STATE_FOCUSING_SNAP_ON_FINISH); 454 } 455 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 468 private void capture() { 469 if (mListener.capture()) { 470 mState = STATE_IDLE; 471 mHandler.removeMessages(RESET_TOUCH_FOCUS); 472 } 473 } 474 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 522 public List<Area> getFocusAreas() { 523 return mFocusArea; 524 } 525 526 public List<Area> getMeteringAreas() { 527 return mMeteringArea; 528 } 529 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 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 558 /* package */ int getFocusState() { 559 return mState; 560 } 561 562 public boolean isFocusCompleted() { 563 return mState == STATE_SUCCESS || mState == STATE_FAIL; 564 } 565 566 public boolean isFocusingSnapOnFinish() { 567 return mState == STATE_FOCUSING_SNAP_ON_FINISH; 568 } 569 570 public void removeMessages() { 571 mHandler.removeMessages(RESET_TOUCH_FOCUS); 572 } 573 574 public void overrideFocusMode(CameraCapabilities.FocusMode focusMode) { 575 mOverrideFocusMode = focusMode; 576 } 577 578 public void setAeAwbLock(boolean lock) { 579 mAeAwbLock = lock; 580 } 581 582 public boolean getAeAwbLock() { 583 return mAeAwbLock; 584 } 585 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