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