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 { autoFocus()102 public void autoFocus(); cancelAutoFocus()103 public void cancelAutoFocus(); capture()104 public boolean capture(); startFaceDetection()105 public void startFaceDetection(); stopFaceDetection()106 public void stopFaceDetection(); setFocusParameters()107 public void setFocusParameters(); playSound(int soundId)108 public void playSound(int soundId); 109 } 110 111 private class MainHandler extends Handler { MainHandler(Looper looper)112 public MainHandler(Looper looper) { 113 super(looper); 114 } 115 116 @Override handleMessage(Message msg)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 setFocusAreaIndicator(View l)128 public void setFocusAreaIndicator(View l) { 129 mFocusIndicatorRotateLayout = (FocusIndicatorRotateLayout) l; 130 mFocusIndicator = l.findViewById(R.id.focus_indicator); 131 } 132 FocusManager(ComboPreferences preferences, String[] defaultFocusModes, View focusIndicatorRotate, Parameters parameters, Listener listener, boolean mirror, Looper looper)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 setParameters(Parameters parameters)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 setPreviewSize(int previewWidth, int previewHeight)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 setMirror(boolean mirror)170 public void setMirror(boolean mirror) { 171 mMirror = mirror; 172 setMatrix(); 173 } 174 setDisplayOrientation(int displayOrientation)175 public void setDisplayOrientation(int displayOrientation) { 176 mDisplayOrientation = displayOrientation; 177 setMatrix(); 178 } 179 setFaceView(FaceView faceView)180 public void setFaceView(FaceView faceView) { 181 mFaceView = faceView; 182 } 183 setMatrix()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 onShutterDown()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 onShutterUp()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 doSnap()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 onAutoFocus(boolean focused)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 onAutoFocusMoving(boolean moving)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 onSingleTapUp(int x, int y)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 onPreviewStarted()365 public void onPreviewStarted() { 366 mState = STATE_IDLE; 367 } 368 onPreviewStopped()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 onCameraReleased()376 public void onCameraReleased() { 377 onPreviewStopped(); 378 } 379 autoFocus()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 cancelAutoFocus()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 capture()405 private void capture() { 406 if (mListener.capture()) { 407 mState = STATE_IDLE; 408 mHandler.removeMessages(RESET_TOUCH_FOCUS); 409 } 410 } 411 getFocusMode()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 getFocusAreas()448 public List<Area> getFocusAreas() { 449 return mFocusArea; 450 } 451 getMeteringAreas()452 public List<Area> getMeteringAreas() { 453 return mMeteringArea; 454 } 455 updateFocusUI()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 resetTouchFocus()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 calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, int x, int y, int previewWidth, int previewHeight, Rect rect)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 isFocusCompleted()513 public boolean isFocusCompleted() { 514 return mState == STATE_SUCCESS || mState == STATE_FAIL; 515 } 516 isFocusingSnapOnFinish()517 public boolean isFocusingSnapOnFinish() { 518 return mState == STATE_FOCUSING_SNAP_ON_FINISH; 519 } 520 removeMessages()521 public void removeMessages() { 522 mHandler.removeMessages(RESET_TOUCH_FOCUS); 523 } 524 overrideFocusMode(String focusMode)525 public void overrideFocusMode(String focusMode) { 526 mOverrideFocusMode = focusMode; 527 } 528 setAeAwbLock(boolean lock)529 public void setAeAwbLock(boolean lock) { 530 mAeAwbLock = lock; 531 } 532 getAeAwbLock()533 public boolean getAeAwbLock() { 534 return mAeAwbLock; 535 } 536 isSupported(String value, List<String> supported)537 private static boolean isSupported(String value, List<String> supported) { 538 return supported == null ? false : supported.indexOf(value) >= 0; 539 } 540 needAutoFocusCall()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