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