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