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