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.media.MediaActionSound; 30 import android.os.Handler; 31 import android.os.Message; 32 import android.util.Log; 33 import android.view.MotionEvent; 34 import android.view.View; 35 import android.view.ViewGroup; 36 import android.widget.RelativeLayout; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 41 // A class that handles everything about focus in still picture mode. 42 // This also handles the metering area because it is the same as focus area. 43 public class FocusManager { 44 private static final String TAG = "FocusManager"; 45 46 private static final int RESET_TOUCH_FOCUS = 0; 47 private static final int FOCUS_BEEP_VOLUME = 100; 48 private static final int RESET_TOUCH_FOCUS_DELAY = 3000; 49 50 private int mState = STATE_IDLE; 51 private static final int STATE_IDLE = 0; // Focus is not active. 52 private static final int STATE_FOCUSING = 1; // Focus is in progress. 53 // Focus is in progress and the camera should take a picture after focus finishes. 54 private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2; 55 private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds. 56 private static final int STATE_FAIL = 4; // Focus finishes and fails. 57 58 private boolean mInitialized; 59 private boolean mFocusAreaSupported; 60 private boolean mLockAeAwbNeeded; 61 private boolean mAeAwbLock; 62 private Matrix mMatrix; 63 private View mFocusIndicatorRotateLayout; 64 private FocusIndicatorView mFocusIndicator; 65 private View mPreviewFrame; 66 private FaceView mFaceView; 67 private List<Area> mFocusArea; // focus area in driver format 68 private List<Area> mMeteringArea; // metering area in driver format 69 private String mFocusMode; 70 private String[] mDefaultFocusModes; 71 private String mOverrideFocusMode; 72 private Parameters mParameters; 73 private ComboPreferences mPreferences; 74 private Handler mHandler; 75 Listener mListener; 76 77 public interface Listener { 78 public void autoFocus(); 79 public void cancelAutoFocus(); 80 public boolean capture(); 81 public void startFaceDetection(); 82 public void stopFaceDetection(); 83 public void setFocusParameters(); 84 public void playSound(int soundId); 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[] defaultFocusModes) { 101 mPreferences = preferences; 102 mDefaultFocusModes = defaultFocusModes; 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 doSnap() { 178 if (!mInitialized) return; 179 180 // If the user has half-pressed the shutter and focus is completed, we 181 // can take the photo right away. If the focus mode is infinity, we can 182 // also take the photo. 183 if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) { 184 capture(); 185 } else if (mState == STATE_FOCUSING) { 186 // Half pressing the shutter (i.e. the focus button event) will 187 // already have requested AF for us, so just request capture on 188 // focus here. 189 mState = STATE_FOCUSING_SNAP_ON_FINISH; 190 } else if (mState == STATE_IDLE) { 191 // We didn't do focus. This can happen if the user press focus key 192 // while the snapshot is still in progress. The user probably wants 193 // the next snapshot as soon as possible, so we just do a snapshot 194 // without focusing again. 195 capture(); 196 } 197 } 198 199 public void onShutter() { 200 resetTouchFocus(); 201 updateFocusUI(); 202 } 203 204 public void onAutoFocus(boolean focused) { 205 if (mState == STATE_FOCUSING_SNAP_ON_FINISH) { 206 // Take the picture no matter focus succeeds or fails. No need 207 // to play the AF sound if we're about to play the shutter 208 // sound. 209 if (focused) { 210 mState = STATE_SUCCESS; 211 } else { 212 mState = STATE_FAIL; 213 } 214 updateFocusUI(); 215 capture(); 216 } else if (mState == STATE_FOCUSING) { 217 // This happens when (1) user is half-pressing the focus key or 218 // (2) touch focus is triggered. Play the focus tone. Do not 219 // take the picture now. 220 if (focused) { 221 mState = STATE_SUCCESS; 222 // Do not play the sound in continuous autofocus mode. It does 223 // not do a full scan. The focus callback arrives before doSnap 224 // so the state is always STATE_FOCUSING. 225 if (!Parameters.FOCUS_MODE_CONTINUOUS_PICTURE. 226 equals(mFocusMode)) { 227 mListener.playSound(MediaActionSound.FOCUS_COMPLETE); 228 } 229 } else { 230 mState = STATE_FAIL; 231 } 232 updateFocusUI(); 233 // If this is triggered by touch focus, cancel focus after a 234 // while. 235 if (mFocusArea != null) { 236 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); 237 } 238 } else if (mState == STATE_IDLE) { 239 // User has released the focus key before focus completes. 240 // Do nothing. 241 } 242 } 243 244 public boolean onTouch(MotionEvent e) { 245 if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) return false; 246 247 // Let users be able to cancel previous touch focus. 248 if ((mFocusArea != null) && (mState == STATE_FOCUSING || 249 mState == STATE_SUCCESS || mState == STATE_FAIL)) { 250 cancelAutoFocus(); 251 } 252 253 // Initialize variables. 254 int x = Math.round(e.getX()); 255 int y = Math.round(e.getY()); 256 int focusWidth = mFocusIndicatorRotateLayout.getWidth(); 257 int focusHeight = mFocusIndicatorRotateLayout.getHeight(); 258 int previewWidth = mPreviewFrame.getWidth(); 259 int previewHeight = mPreviewFrame.getHeight(); 260 if (mFocusArea == null) { 261 mFocusArea = new ArrayList<Area>(); 262 mFocusArea.add(new Area(new Rect(), 1)); 263 mMeteringArea = new ArrayList<Area>(); 264 mMeteringArea.add(new Area(new Rect(), 1)); 265 } 266 267 // Convert the coordinates to driver format. 268 // AE area is bigger because exposure is sensitive and 269 // easy to over- or underexposure if area is too small. 270 calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight, 271 mFocusArea.get(0).rect); 272 calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight, 273 mMeteringArea.get(0).rect); 274 275 // Use margin to set the focus indicator to the touched area. 276 RelativeLayout.LayoutParams p = 277 (RelativeLayout.LayoutParams) mFocusIndicatorRotateLayout.getLayoutParams(); 278 int left = Util.clamp(x - focusWidth / 2, 0, previewWidth - focusWidth); 279 int top = Util.clamp(y - focusHeight / 2, 0, previewHeight - focusHeight); 280 p.setMargins(left, top, 0, 0); 281 // Disable "center" rule because we no longer want to put it in the center. 282 int[] rules = p.getRules(); 283 rules[RelativeLayout.CENTER_IN_PARENT] = 0; 284 mFocusIndicatorRotateLayout.requestLayout(); 285 286 // Stop face detection because we want to specify focus and metering area. 287 mListener.stopFaceDetection(); 288 289 // Set the focus area and metering area. 290 mListener.setFocusParameters(); 291 if (mFocusAreaSupported && (e.getAction() == MotionEvent.ACTION_UP)) { 292 autoFocus(); 293 } else { // Just show the indicator in all other cases. 294 updateFocusUI(); 295 // Reset the metering area in 3 seconds. 296 mHandler.removeMessages(RESET_TOUCH_FOCUS); 297 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); 298 } 299 300 return true; 301 } 302 303 public void onPreviewStarted() { 304 mState = STATE_IDLE; 305 } 306 307 public void onPreviewStopped() { 308 mState = STATE_IDLE; 309 resetTouchFocus(); 310 // If auto focus was in progress, it would have been canceled. 311 updateFocusUI(); 312 } 313 314 public void onCameraReleased() { 315 onPreviewStopped(); 316 } 317 318 private void autoFocus() { 319 Log.v(TAG, "Start autofocus."); 320 mListener.autoFocus(); 321 mState = STATE_FOCUSING; 322 // Pause the face view because the driver will keep sending face 323 // callbacks after the focus completes. 324 if (mFaceView != null) mFaceView.pause(); 325 updateFocusUI(); 326 mHandler.removeMessages(RESET_TOUCH_FOCUS); 327 } 328 329 private void cancelAutoFocus() { 330 Log.v(TAG, "Cancel autofocus."); 331 332 // Reset the tap area before calling mListener.cancelAutofocus. 333 // Otherwise, focus mode stays at auto and the tap area passed to the 334 // driver is not reset. 335 resetTouchFocus(); 336 mListener.cancelAutoFocus(); 337 if (mFaceView != null) mFaceView.resume(); 338 mState = STATE_IDLE; 339 updateFocusUI(); 340 mHandler.removeMessages(RESET_TOUCH_FOCUS); 341 } 342 343 private void capture() { 344 if (mListener.capture()) { 345 mState = STATE_IDLE; 346 mHandler.removeMessages(RESET_TOUCH_FOCUS); 347 } 348 } 349 350 // This can only be called after mParameters is initialized. 351 public String getFocusMode() { 352 if (mOverrideFocusMode != null) return mOverrideFocusMode; 353 List<String> supportedFocusModes = mParameters.getSupportedFocusModes(); 354 355 if (mFocusAreaSupported && mFocusArea != null) { 356 // Always use autofocus in tap-to-focus. 357 mFocusMode = Parameters.FOCUS_MODE_AUTO; 358 } else { 359 // The default is continuous autofocus. 360 mFocusMode = mPreferences.getString( 361 CameraSettings.KEY_FOCUS_MODE, null); 362 363 // Try to find a supported focus mode from the default list. 364 if (mFocusMode == null) { 365 for (int i = 0; i < mDefaultFocusModes.length; i++) { 366 String mode = mDefaultFocusModes[i]; 367 if (isSupported(mode, supportedFocusModes)) { 368 mFocusMode = mode; 369 break; 370 } 371 } 372 } 373 } 374 if (!isSupported(mFocusMode, supportedFocusModes)) { 375 // For some reasons, the driver does not support the current 376 // focus mode. Fall back to auto. 377 if (isSupported(Parameters.FOCUS_MODE_AUTO, 378 mParameters.getSupportedFocusModes())) { 379 mFocusMode = Parameters.FOCUS_MODE_AUTO; 380 } else { 381 mFocusMode = mParameters.getFocusMode(); 382 } 383 } 384 return mFocusMode; 385 } 386 387 public List<Area> getFocusAreas() { 388 return mFocusArea; 389 } 390 391 public List<Area> getMeteringAreas() { 392 return mMeteringArea; 393 } 394 395 public void updateFocusUI() { 396 if (!mInitialized) return; 397 398 // Set the length of focus indicator according to preview frame size. 399 int len = Math.min(mPreviewFrame.getWidth(), mPreviewFrame.getHeight()) / 4; 400 ViewGroup.LayoutParams layout = mFocusIndicator.getLayoutParams(); 401 layout.width = len; 402 layout.height = len; 403 404 // Show only focus indicator or face indicator. 405 boolean faceExists = (mFaceView != null && mFaceView.faceExists()); 406 FocusIndicator focusIndicator = (faceExists) ? mFaceView : mFocusIndicator; 407 408 if (mState == STATE_IDLE) { 409 if (mFocusArea == null) { 410 focusIndicator.clear(); 411 } else { 412 // Users touch on the preview and the indicator represents the 413 // metering area. Either focus area is not supported or 414 // autoFocus call is not required. 415 focusIndicator.showStart(); 416 } 417 } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) { 418 focusIndicator.showStart(); 419 } else { 420 // In CAF, do not show success or failure because it only returns 421 // the focus status. It does not do a full scan. So the result is 422 // failure most of the time. 423 if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) { 424 focusIndicator.showStart(); 425 } else if (mState == STATE_SUCCESS) { 426 focusIndicator.showSuccess(); 427 } else if (mState == STATE_FAIL) { 428 focusIndicator.showFail(); 429 } 430 } 431 } 432 433 public void resetTouchFocus() { 434 if (!mInitialized) return; 435 436 // Put focus indicator to the center. 437 RelativeLayout.LayoutParams p = 438 (RelativeLayout.LayoutParams) mFocusIndicatorRotateLayout.getLayoutParams(); 439 int[] rules = p.getRules(); 440 rules[RelativeLayout.CENTER_IN_PARENT] = RelativeLayout.TRUE; 441 p.setMargins(0, 0, 0, 0); 442 443 mFocusArea = null; 444 mMeteringArea = null; 445 } 446 447 public void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, 448 int x, int y, int previewWidth, int previewHeight, Rect rect) { 449 int areaWidth = (int)(focusWidth * areaMultiple); 450 int areaHeight = (int)(focusHeight * areaMultiple); 451 int left = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth); 452 int top = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight); 453 454 RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight); 455 mMatrix.mapRect(rectF); 456 Util.rectFToRect(rectF, rect); 457 } 458 459 public boolean isFocusCompleted() { 460 return mState == STATE_SUCCESS || mState == STATE_FAIL; 461 } 462 463 public boolean isFocusingSnapOnFinish() { 464 return mState == STATE_FOCUSING_SNAP_ON_FINISH; 465 } 466 467 public void removeMessages() { 468 mHandler.removeMessages(RESET_TOUCH_FOCUS); 469 } 470 471 public void overrideFocusMode(String focusMode) { 472 mOverrideFocusMode = focusMode; 473 } 474 475 public void setAeAwbLock(boolean lock) { 476 mAeAwbLock = lock; 477 } 478 479 public boolean getAeAwbLock() { 480 return mAeAwbLock; 481 } 482 483 private static boolean isSupported(String value, List<String> supported) { 484 return supported == null ? false : supported.indexOf(value) >= 0; 485 } 486 487 private boolean needAutoFocusCall() { 488 String focusMode = getFocusMode(); 489 return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY) 490 || focusMode.equals(Parameters.FOCUS_MODE_FIXED) 491 || focusMode.equals(Parameters.FOCUS_MODE_EDOF)); 492 } 493 } 494