1/* 2 * Copyright (C) 2015 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 17package com.android.messaging.ui.mediapicker.camerafocus; 18 19import android.graphics.Matrix; 20import android.graphics.Rect; 21import android.graphics.RectF; 22import android.hardware.Camera.Area; 23import android.hardware.Camera.Parameters; 24import android.os.Handler; 25import android.os.Looper; 26import android.os.Message; 27 28import com.android.messaging.util.Assert; 29import com.android.messaging.util.LogUtil; 30 31import java.util.ArrayList; 32import java.util.List; 33 34/* A class that handles everything about focus in still picture mode. 35 * This also handles the metering area because it is the same as focus area. 36 * 37 * The test cases: 38 * (1) The camera has continuous autofocus. Move the camera. Take a picture when 39 * CAF is not in progress. 40 * (2) The camera has continuous autofocus. Move the camera. Take a picture when 41 * CAF is in progress. 42 * (3) The camera has face detection. Point the camera at some faces. Hold the 43 * shutter. Release to take a picture. 44 * (4) The camera has face detection. Point the camera at some faces. Single tap 45 * the shutter to take a picture. 46 * (5) The camera has autofocus. Single tap the shutter to take a picture. 47 * (6) The camera has autofocus. Hold the shutter. Release to take a picture. 48 * (7) The camera has no autofocus. Single tap the shutter and take a picture. 49 * (8) The camera has autofocus and supports focus area. Touch the screen to 50 * trigger autofocus. Take a picture. 51 * (9) The camera has autofocus and supports focus area. Touch the screen to 52 * trigger autofocus. Wait until it times out. 53 * (10) The camera has no autofocus and supports metering area. Touch the screen 54 * to change metering area. 55 */ 56public class FocusOverlayManager { 57 private static final String TAG = LogUtil.BUGLE_TAG; 58 private static final String TRUE = "true"; 59 private static final String AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported"; 60 private static final String AUTO_WHITE_BALANCE_LOCK_SUPPORTED = 61 "auto-whitebalance-lock-supported"; 62 63 private static final int RESET_TOUCH_FOCUS = 0; 64 private static final int RESET_TOUCH_FOCUS_DELAY = 3000; 65 66 private int mState = STATE_IDLE; 67 private static final int STATE_IDLE = 0; // Focus is not active. 68 private static final int STATE_FOCUSING = 1; // Focus is in progress. 69 // Focus is in progress and the camera should take a picture after focus finishes. 70 private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2; 71 private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds. 72 private static final int STATE_FAIL = 4; // Focus finishes and fails. 73 74 private boolean mInitialized; 75 private boolean mFocusAreaSupported; 76 private boolean mMeteringAreaSupported; 77 private boolean mLockAeAwbNeeded; 78 private boolean mAeAwbLock; 79 private Matrix mMatrix; 80 81 private PieRenderer mPieRenderer; 82 83 private int mPreviewWidth; // The width of the preview frame layout. 84 private int mPreviewHeight; // The height of the preview frame layout. 85 private boolean mMirror; // true if the camera is front-facing. 86 private int mDisplayOrientation; 87 private List<Object> mFocusArea; // focus area in driver format 88 private List<Object> mMeteringArea; // metering area in driver format 89 private String mFocusMode; 90 private String mOverrideFocusMode; 91 private Parameters mParameters; 92 private Handler mHandler; 93 Listener mListener; 94 95 public interface Listener { 96 public void autoFocus(); 97 public void cancelAutoFocus(); 98 public boolean capture(); 99 public void setFocusParameters(); 100 } 101 102 private class MainHandler extends Handler { 103 public MainHandler(Looper looper) { 104 super(looper); 105 } 106 107 @Override 108 public void handleMessage(Message msg) { 109 switch (msg.what) { 110 case RESET_TOUCH_FOCUS: { 111 cancelAutoFocus(); 112 break; 113 } 114 } 115 } 116 } 117 118 public FocusOverlayManager(Listener listener, Looper looper) { 119 mHandler = new MainHandler(looper); 120 mMatrix = new Matrix(); 121 mListener = listener; 122 } 123 124 public void setFocusRenderer(PieRenderer renderer) { 125 mPieRenderer = renderer; 126 mInitialized = (mMatrix != null); 127 } 128 129 public void setParameters(Parameters parameters) { 130 // parameters can only be null when onConfigurationChanged is called 131 // before camera is open. We will just return in this case, because 132 // parameters will be set again later with the right parameters after 133 // camera is open. 134 if (parameters == null) { 135 return; 136 } 137 mParameters = parameters; 138 mFocusAreaSupported = isFocusAreaSupported(parameters); 139 mMeteringAreaSupported = isMeteringAreaSupported(parameters); 140 mLockAeAwbNeeded = (isAutoExposureLockSupported(mParameters) || 141 isAutoWhiteBalanceLockSupported(mParameters)); 142 } 143 144 public void setPreviewSize(int previewWidth, int previewHeight) { 145 if (mPreviewWidth != previewWidth || mPreviewHeight != previewHeight) { 146 mPreviewWidth = previewWidth; 147 mPreviewHeight = previewHeight; 148 setMatrix(); 149 } 150 } 151 152 public void setMirror(boolean mirror) { 153 mMirror = mirror; 154 setMatrix(); 155 } 156 157 public void setDisplayOrientation(int displayOrientation) { 158 mDisplayOrientation = displayOrientation; 159 setMatrix(); 160 } 161 162 private void setMatrix() { 163 if (mPreviewWidth != 0 && mPreviewHeight != 0) { 164 Matrix matrix = new Matrix(); 165 prepareMatrix(matrix, mMirror, mDisplayOrientation, 166 mPreviewWidth, mPreviewHeight); 167 // In face detection, the matrix converts the driver coordinates to UI 168 // coordinates. In tap focus, the inverted matrix converts the UI 169 // coordinates to driver coordinates. 170 matrix.invert(mMatrix); 171 mInitialized = (mPieRenderer != null); 172 } 173 } 174 175 private void lockAeAwbIfNeeded() { 176 if (mLockAeAwbNeeded && !mAeAwbLock) { 177 mAeAwbLock = true; 178 mListener.setFocusParameters(); 179 } 180 } 181 182 private void unlockAeAwbIfNeeded() { 183 if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) { 184 mAeAwbLock = false; 185 mListener.setFocusParameters(); 186 } 187 } 188 189 public void onShutterDown() { 190 if (!mInitialized) { 191 return; 192 } 193 194 boolean autoFocusCalled = false; 195 if (needAutoFocusCall()) { 196 // Do not focus if touch focus has been triggered. 197 if (mState != STATE_SUCCESS && mState != STATE_FAIL) { 198 autoFocus(); 199 autoFocusCalled = true; 200 } 201 } 202 203 if (!autoFocusCalled) { 204 lockAeAwbIfNeeded(); 205 } 206 } 207 208 public void onShutterUp() { 209 if (!mInitialized) { 210 return; 211 } 212 213 if (needAutoFocusCall()) { 214 // User releases half-pressed focus key. 215 if (mState == STATE_FOCUSING || mState == STATE_SUCCESS 216 || mState == STATE_FAIL) { 217 cancelAutoFocus(); 218 } 219 } 220 221 // Unlock AE and AWB after cancelAutoFocus. Camera API does not 222 // guarantee setParameters can be called during autofocus. 223 unlockAeAwbIfNeeded(); 224 } 225 226 public void doSnap() { 227 if (!mInitialized) { 228 return; 229 } 230 231 // If the user has half-pressed the shutter and focus is completed, we 232 // can take the photo right away. If the focus mode is infinity, we can 233 // also take the photo. 234 if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) { 235 capture(); 236 } else if (mState == STATE_FOCUSING) { 237 // Half pressing the shutter (i.e. the focus button event) will 238 // already have requested AF for us, so just request capture on 239 // focus here. 240 mState = STATE_FOCUSING_SNAP_ON_FINISH; 241 } else if (mState == STATE_IDLE) { 242 // We didn't do focus. This can happen if the user press focus key 243 // while the snapshot is still in progress. The user probably wants 244 // the next snapshot as soon as possible, so we just do a snapshot 245 // without focusing again. 246 capture(); 247 } 248 } 249 250 public void onAutoFocus(boolean focused, boolean shutterButtonPressed) { 251 if (mState == STATE_FOCUSING_SNAP_ON_FINISH) { 252 // Take the picture no matter focus succeeds or fails. No need 253 // to play the AF sound if we're about to play the shutter 254 // sound. 255 if (focused) { 256 mState = STATE_SUCCESS; 257 } else { 258 mState = STATE_FAIL; 259 } 260 updateFocusUI(); 261 capture(); 262 } else if (mState == STATE_FOCUSING) { 263 // This happens when (1) user is half-pressing the focus key or 264 // (2) touch focus is triggered. Play the focus tone. Do not 265 // take the picture now. 266 if (focused) { 267 mState = STATE_SUCCESS; 268 } else { 269 mState = STATE_FAIL; 270 } 271 updateFocusUI(); 272 // If this is triggered by touch focus, cancel focus after a 273 // while. 274 if (mFocusArea != null) { 275 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); 276 } 277 if (shutterButtonPressed) { 278 // Lock AE & AWB so users can half-press shutter and recompose. 279 lockAeAwbIfNeeded(); 280 } 281 } else if (mState == STATE_IDLE) { 282 // User has released the focus key before focus completes. 283 // Do nothing. 284 } 285 } 286 287 public void onAutoFocusMoving(boolean moving) { 288 if (!mInitialized) { 289 return; 290 } 291 292 // Ignore if we have requested autofocus. This method only handles 293 // continuous autofocus. 294 if (mState != STATE_IDLE) { 295 return; 296 } 297 298 if (moving) { 299 mPieRenderer.showStart(); 300 } else { 301 mPieRenderer.showSuccess(true); 302 } 303 } 304 305 private void initializeFocusAreas(int focusWidth, int focusHeight, 306 int x, int y, int previewWidth, int previewHeight) { 307 if (mFocusArea == null) { 308 mFocusArea = new ArrayList<Object>(); 309 mFocusArea.add(new Area(new Rect(), 1)); 310 } 311 312 // Convert the coordinates to driver format. 313 calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight, 314 ((Area) mFocusArea.get(0)).rect); 315 } 316 317 private void initializeMeteringAreas(int focusWidth, int focusHeight, 318 int x, int y, int previewWidth, int previewHeight) { 319 if (mMeteringArea == null) { 320 mMeteringArea = new ArrayList<Object>(); 321 mMeteringArea.add(new Area(new Rect(), 1)); 322 } 323 324 // Convert the coordinates to driver format. 325 // AE area is bigger because exposure is sensitive and 326 // easy to over- or underexposure if area is too small. 327 calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight, 328 ((Area) mMeteringArea.get(0)).rect); 329 } 330 331 public void onSingleTapUp(int x, int y) { 332 if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) { 333 return; 334 } 335 336 // Let users be able to cancel previous touch focus. 337 if ((mFocusArea != null) && (mState == STATE_FOCUSING || 338 mState == STATE_SUCCESS || mState == STATE_FAIL)) { 339 cancelAutoFocus(); 340 } 341 // Initialize variables. 342 int focusWidth = mPieRenderer.getSize(); 343 int focusHeight = mPieRenderer.getSize(); 344 if (focusWidth == 0 || mPieRenderer.getWidth() == 0 || mPieRenderer.getHeight() == 0) { 345 return; 346 } 347 int previewWidth = mPreviewWidth; 348 int previewHeight = mPreviewHeight; 349 // Initialize mFocusArea. 350 if (mFocusAreaSupported) { 351 initializeFocusAreas(focusWidth, focusHeight, x, y, previewWidth, previewHeight); 352 } 353 // Initialize mMeteringArea. 354 if (mMeteringAreaSupported) { 355 initializeMeteringAreas(focusWidth, focusHeight, x, y, previewWidth, previewHeight); 356 } 357 358 // Use margin to set the focus indicator to the touched area. 359 mPieRenderer.setFocus(x, y); 360 361 // Set the focus area and metering area. 362 mListener.setFocusParameters(); 363 if (mFocusAreaSupported) { 364 autoFocus(); 365 } else { // Just show the indicator in all other cases. 366 updateFocusUI(); 367 // Reset the metering area in 3 seconds. 368 mHandler.removeMessages(RESET_TOUCH_FOCUS); 369 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); 370 } 371 } 372 373 public void onPreviewStarted() { 374 mState = STATE_IDLE; 375 } 376 377 public void onPreviewStopped() { 378 // If auto focus was in progress, it would have been stopped. 379 mState = STATE_IDLE; 380 resetTouchFocus(); 381 updateFocusUI(); 382 } 383 384 public void onCameraReleased() { 385 onPreviewStopped(); 386 } 387 388 private void autoFocus() { 389 LogUtil.v(TAG, "Start autofocus."); 390 mListener.autoFocus(); 391 mState = STATE_FOCUSING; 392 updateFocusUI(); 393 mHandler.removeMessages(RESET_TOUCH_FOCUS); 394 } 395 396 private void cancelAutoFocus() { 397 LogUtil.v(TAG, "Cancel autofocus."); 398 399 // Reset the tap area before calling mListener.cancelAutofocus. 400 // Otherwise, focus mode stays at auto and the tap area passed to the 401 // driver is not reset. 402 resetTouchFocus(); 403 mListener.cancelAutoFocus(); 404 mState = STATE_IDLE; 405 updateFocusUI(); 406 mHandler.removeMessages(RESET_TOUCH_FOCUS); 407 } 408 409 private void capture() { 410 if (mListener.capture()) { 411 mState = STATE_IDLE; 412 mHandler.removeMessages(RESET_TOUCH_FOCUS); 413 } 414 } 415 416 public String getFocusMode() { 417 if (mOverrideFocusMode != null) { 418 return mOverrideFocusMode; 419 } 420 List<String> supportedFocusModes = mParameters.getSupportedFocusModes(); 421 422 if (mFocusAreaSupported && mFocusArea != null) { 423 // Always use autofocus in tap-to-focus. 424 mFocusMode = Parameters.FOCUS_MODE_AUTO; 425 } else { 426 mFocusMode = Parameters.FOCUS_MODE_CONTINUOUS_PICTURE; 427 } 428 429 if (!isSupported(mFocusMode, supportedFocusModes)) { 430 // For some reasons, the driver does not support the current 431 // focus mode. Fall back to auto. 432 if (isSupported(Parameters.FOCUS_MODE_AUTO, 433 mParameters.getSupportedFocusModes())) { 434 mFocusMode = Parameters.FOCUS_MODE_AUTO; 435 } else { 436 mFocusMode = mParameters.getFocusMode(); 437 } 438 } 439 return mFocusMode; 440 } 441 442 public List getFocusAreas() { 443 return mFocusArea; 444 } 445 446 public List getMeteringAreas() { 447 return mMeteringArea; 448 } 449 450 public void updateFocusUI() { 451 if (!mInitialized) { 452 return; 453 } 454 FocusIndicator focusIndicator = mPieRenderer; 455 456 if (mState == STATE_IDLE) { 457 if (mFocusArea == null) { 458 focusIndicator.clear(); 459 } else { 460 // Users touch on the preview and the indicator represents the 461 // metering area. Either focus area is not supported or 462 // autoFocus call is not required. 463 focusIndicator.showStart(); 464 } 465 } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) { 466 focusIndicator.showStart(); 467 } else { 468 if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) { 469 // TODO: check HAL behavior and decide if this can be removed. 470 focusIndicator.showSuccess(false); 471 } else if (mState == STATE_SUCCESS) { 472 focusIndicator.showSuccess(false); 473 } else if (mState == STATE_FAIL) { 474 focusIndicator.showFail(false); 475 } 476 } 477 } 478 479 public void resetTouchFocus() { 480 if (!mInitialized) { 481 return; 482 } 483 484 // Put focus indicator to the center. clear reset position 485 mPieRenderer.clear(); 486 487 mFocusArea = null; 488 mMeteringArea = null; 489 } 490 491 private void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple, 492 int x, int y, int previewWidth, int previewHeight, Rect rect) { 493 int areaWidth = (int) (focusWidth * areaMultiple); 494 int areaHeight = (int) (focusHeight * areaMultiple); 495 int left = clamp(x - areaWidth / 2, 0, previewWidth - areaWidth); 496 int top = clamp(y - areaHeight / 2, 0, previewHeight - areaHeight); 497 498 RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight); 499 mMatrix.mapRect(rectF); 500 rectFToRect(rectF, rect); 501 } 502 503 /* package */ int getFocusState() { 504 return mState; 505 } 506 507 public boolean isFocusCompleted() { 508 return mState == STATE_SUCCESS || mState == STATE_FAIL; 509 } 510 511 public boolean isFocusingSnapOnFinish() { 512 return mState == STATE_FOCUSING_SNAP_ON_FINISH; 513 } 514 515 public void removeMessages() { 516 mHandler.removeMessages(RESET_TOUCH_FOCUS); 517 } 518 519 public void overrideFocusMode(String focusMode) { 520 mOverrideFocusMode = focusMode; 521 } 522 523 public void setAeAwbLock(boolean lock) { 524 mAeAwbLock = lock; 525 } 526 527 public boolean getAeAwbLock() { 528 return mAeAwbLock; 529 } 530 531 private boolean needAutoFocusCall() { 532 String focusMode = getFocusMode(); 533 return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY) 534 || focusMode.equals(Parameters.FOCUS_MODE_FIXED) 535 || focusMode.equals(Parameters.FOCUS_MODE_EDOF)); 536 } 537 538 public static boolean isAutoExposureLockSupported(Parameters params) { 539 return TRUE.equals(params.get(AUTO_EXPOSURE_LOCK_SUPPORTED)); 540 } 541 542 public static boolean isAutoWhiteBalanceLockSupported(Parameters params) { 543 return TRUE.equals(params.get(AUTO_WHITE_BALANCE_LOCK_SUPPORTED)); 544 } 545 546 public static boolean isSupported(String value, List<String> supported) { 547 return supported != null && supported.indexOf(value) >= 0; 548 } 549 550 public static boolean isMeteringAreaSupported(Parameters params) { 551 return params.getMaxNumMeteringAreas() > 0; 552 } 553 554 public static boolean isFocusAreaSupported(Parameters params) { 555 return (params.getMaxNumFocusAreas() > 0 556 && isSupported(Parameters.FOCUS_MODE_AUTO, 557 params.getSupportedFocusModes())); 558 } 559 560 public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation, 561 int viewWidth, int viewHeight) { 562 // Need mirror for front camera. 563 matrix.setScale(mirror ? -1 : 1, 1); 564 // This is the value for android.hardware.Camera.setDisplayOrientation. 565 matrix.postRotate(displayOrientation); 566 // Camera driver coordinates range from (-1000, -1000) to (1000, 1000). 567 // UI coordinates range from (0, 0) to (width, height). 568 matrix.postScale(viewWidth / 2000f, viewHeight / 2000f); 569 matrix.postTranslate(viewWidth / 2f, viewHeight / 2f); 570 } 571 572 public static int clamp(int x, int min, int max) { 573 Assert.isTrue(max >= min); 574 if (x > max) { 575 return max; 576 } 577 if (x < min) { 578 return min; 579 } 580 return x; 581 } 582 583 public static void rectFToRect(RectF rectF, Rect rect) { 584 rect.left = Math.round(rectF.left); 585 rect.top = Math.round(rectF.top); 586 rect.right = Math.round(rectF.right); 587 rect.bottom = Math.round(rectF.bottom); 588 } 589} 590