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