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