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