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 { autoFocus()79 public void autoFocus(); cancelAutoFocus()80 public void cancelAutoFocus(); capture()81 public boolean capture(); startFaceDetection()82 public void startFaceDetection(); stopFaceDetection()83 public void stopFaceDetection(); setFocusParameters()84 public void setFocusParameters(); 85 } 86 87 private class MainHandler extends Handler { 88 @Override handleMessage(Message msg)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 FocusManager(ComboPreferences preferences, String defaultFocusMode)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(). initializeParameters(Parameters parameters)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 initialize(View focusIndicatorRotate, View previewFrame, FaceView faceView, Listener listener, boolean mirror, int displayOrientation)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 onShutterDown()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 onShutterUp()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 shutterLongPressed()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 doSnap()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 onShutter()215 public void onShutter() { 216 resetTouchFocus(); 217 updateFocusUI(); 218 } 219 onAutoFocus(boolean focused)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 onTouch(MotionEvent e)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 onPreviewStarted()319 public void onPreviewStarted() { 320 mState = STATE_IDLE; 321 } 322 onPreviewStopped()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 onCameraReleased()330 public void onCameraReleased() { 331 onPreviewStopped(); 332 } 333 autoFocus()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 cancelAutoFocus()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 capture()359 private void capture() { 360 if (mListener.capture()) { 361 mState = STATE_IDLE; 362 mHandler.removeMessages(RESET_TOUCH_FOCUS); 363 } 364 } 365 initializeSoundPlayer(AssetFileDescriptor fd)366 public void initializeSoundPlayer(AssetFileDescriptor fd) { 367 mSoundPlayer = new SoundPlayer(fd); 368 } 369 releaseSoundPlayer()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. getFocusMode()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 getFocusAreas()407 public List<Area> getFocusAreas() { 408 return mFocusArea; 409 } 410 getMeteringAreas()411 public List<Area> getMeteringAreas() { 412 return mMeteringArea; 413 } 414 updateFocusUI()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 resetTouchFocus()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 calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, int x, int y, int previewWidth, int previewHeight, Rect rect)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 isFocusCompleted()479 public boolean isFocusCompleted() { 480 return mState == STATE_SUCCESS || mState == STATE_FAIL; 481 } 482 removeMessages()483 public void removeMessages() { 484 mHandler.removeMessages(RESET_TOUCH_FOCUS); 485 } 486 overrideFocusMode(String focusMode)487 public void overrideFocusMode(String focusMode) { 488 mOverrideFocusMode = focusMode; 489 } 490 setAeAwbLock(boolean lock)491 public void setAeAwbLock(boolean lock) { 492 mAeAwbLock = lock; 493 } 494 getAeAwbLock()495 public boolean getAeAwbLock() { 496 return mAeAwbLock; 497 } 498 isSupported(String value, List<String> supported)499 private static boolean isSupported(String value, List<String> supported) { 500 return supported == null ? false : supported.indexOf(value) >= 0; 501 } 502 needAutoFocusCall()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