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