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