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 { autoFocus()78 public void autoFocus(); cancelAutoFocus()79 public void cancelAutoFocus(); capture()80 public boolean capture(); startFaceDetection()81 public void startFaceDetection(); stopFaceDetection()82 public void stopFaceDetection(); setFocusParameters()83 public void setFocusParameters(); playSound(int soundId)84 public void playSound(int soundId); 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[] defaultFocusModes)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(). 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 doSnap()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 onShutter()199 public void onShutter() { 200 resetTouchFocus(); 201 updateFocusUI(); 202 } 203 onAutoFocus(boolean focused)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 onTouch(MotionEvent e)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 onPreviewStarted()303 public void onPreviewStarted() { 304 mState = STATE_IDLE; 305 } 306 onPreviewStopped()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 onCameraReleased()314 public void onCameraReleased() { 315 onPreviewStopped(); 316 } 317 autoFocus()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 cancelAutoFocus()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 capture()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. getFocusMode()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 getFocusAreas()387 public List<Area> getFocusAreas() { 388 return mFocusArea; 389 } 390 getMeteringAreas()391 public List<Area> getMeteringAreas() { 392 return mMeteringArea; 393 } 394 updateFocusUI()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 resetTouchFocus()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 calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, int x, int y, int previewWidth, int previewHeight, Rect rect)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 isFocusCompleted()459 public boolean isFocusCompleted() { 460 return mState == STATE_SUCCESS || mState == STATE_FAIL; 461 } 462 isFocusingSnapOnFinish()463 public boolean isFocusingSnapOnFinish() { 464 return mState == STATE_FOCUSING_SNAP_ON_FINISH; 465 } 466 removeMessages()467 public void removeMessages() { 468 mHandler.removeMessages(RESET_TOUCH_FOCUS); 469 } 470 overrideFocusMode(String focusMode)471 public void overrideFocusMode(String focusMode) { 472 mOverrideFocusMode = focusMode; 473 } 474 setAeAwbLock(boolean lock)475 public void setAeAwbLock(boolean lock) { 476 mAeAwbLock = lock; 477 } 478 getAeAwbLock()479 public boolean getAeAwbLock() { 480 return mAeAwbLock; 481 } 482 isSupported(String value, List<String> supported)483 private static boolean isSupported(String value, List<String> supported) { 484 return supported == null ? false : supported.indexOf(value) >= 0; 485 } 486 needAutoFocusCall()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