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