PhotoUI.java revision e3974bb4614d027e88ad915f70c4c05fb5ff76b1
1/* 2 * Copyright (C) 2013 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 18package com.android.camera; 19 20import android.hardware.Camera; 21import android.hardware.Camera.Face; 22import android.hardware.Camera.FaceDetectionListener; 23import android.hardware.Camera.Parameters; 24import android.util.Log; 25import android.view.Gravity; 26import android.view.MotionEvent; 27import android.view.SurfaceHolder; 28import android.view.View; 29import android.view.View.OnClickListener; 30import android.view.View.OnLayoutChangeListener; 31import android.view.ViewGroup; 32import android.view.ViewStub; 33import android.widget.FrameLayout; 34import android.widget.FrameLayout.LayoutParams; 35import android.widget.ImageView; 36import android.widget.Toast; 37 38import com.android.camera.CameraPreference.OnPreferenceChangedListener; 39import com.android.camera.FocusOverlayManager.FocusUI; 40import com.android.camera.ui.AbstractSettingPopup; 41import com.android.camera.ui.CountDownView; 42import com.android.camera.ui.CountDownView.OnCountDownFinishedListener; 43import com.android.camera.ui.FaceView; 44import com.android.camera.ui.FocusIndicator; 45import com.android.camera.ui.PieRenderer; 46import com.android.camera.ui.PieRenderer.PieListener; 47import com.android.camera.ui.RenderOverlay; 48import com.android.camera.ui.ZoomRenderer; 49import com.android.gallery3d.R; 50import com.android.gallery3d.common.ApiHelper; 51 52import java.util.List; 53 54public class PhotoUI implements PieListener, 55 SurfaceHolder.Callback, 56 PreviewGestures.SingleTapListener, 57 FocusUI, 58 LocationManager.Listener, 59 FaceDetectionListener { 60 61 private static final String TAG = "CAM_UI"; 62 63 private CameraActivity mActivity; 64 private PhotoController mController; 65 private PreviewGestures mGestures; 66 67 private View mRootView; 68 private Object mSurfaceTexture; 69 private volatile SurfaceHolder mSurfaceHolder; 70 71 private AbstractSettingPopup mPopup; 72 private ShutterButton mShutterButton; 73 private CountDownView mCountDownView; 74 75 private FaceView mFaceView; 76 private RenderOverlay mRenderOverlay; 77 private View mReviewCancelButton; 78 private View mReviewDoneButton; 79 private View mReviewRetakeButton; 80 81 private View mMenuButton; 82 private View mBlocker; 83 private PhotoMenu mMenu; 84 85 // Small indicators which show the camera settings in the viewfinder. 86 private ImageView mExposureIndicator; 87 private ImageView mFlashIndicator; 88 private ImageView mSceneIndicator; 89 private ImageView mHdrIndicator; 90 // A view group that contains all the small indicators. 91 private View mOnScreenIndicators; 92 93 private PieRenderer mPieRenderer; 94 private ZoomRenderer mZoomRenderer; 95 private Toast mNotSelectableToast; 96 97 private int mZoomMax; 98 private List<Integer> mZoomRatios; 99 100 private int mPreviewWidth = 0; 101 private int mPreviewHeight = 0; 102 103 private OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() { 104 @Override 105 public void onLayoutChange(View v, int left, int top, int right, 106 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 107 int width = right - left; 108 int height = bottom - top; 109 // Full-screen screennail 110 int w = width; 111 int h = height; 112 if (Util.getDisplayRotation(mActivity) % 180 != 0) { 113 w = height; 114 h = width; 115 } 116 if (mPreviewWidth != w || mPreviewHeight != h) { 117 mController.onScreenSizeChanged(width, height, w, h); 118 } 119 } 120 }; 121 122 public PhotoUI(CameraActivity activity, PhotoController controller, View parent) { 123 mActivity = activity; 124 mController = controller; 125 mRootView = parent; 126 127 mActivity.getLayoutInflater().inflate(R.layout.photo_module, 128 (ViewGroup) mRootView, true); 129 mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); 130 131 initIndicators(); 132 mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture)); 133 mCountDownView.setCountDownFinishedListener((OnCountDownFinishedListener) mController); 134 135 if (ApiHelper.HAS_FACE_DETECTION) { 136 ViewStub faceViewStub = (ViewStub) mRootView 137 .findViewById(R.id.face_view_stub); 138 if (faceViewStub != null) { 139 faceViewStub.inflate(); 140 mFaceView = (FaceView) mRootView.findViewById(R.id.face_view); 141 } 142 } 143 144 mRootView.addOnLayoutChangeListener(mLayoutListener); 145 } 146 147 public View getRootView() { 148 return mRootView; 149 } 150 151 private void initIndicators() { 152 mOnScreenIndicators = mActivity.findViewById(R.id.on_screen_indicators); 153 mExposureIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_exposure_indicator); 154 mFlashIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_flash_indicator); 155 mSceneIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_scenemode_indicator); 156 mHdrIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_hdr_indicator); 157 } 158 159 public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs, 160 Camera.Parameters params, OnPreferenceChangedListener listener) { 161 if (mPieRenderer == null) { 162 mPieRenderer = new PieRenderer(mActivity); 163 mPieRenderer.setPieListener(this); 164 mRenderOverlay.addRenderer(mPieRenderer); 165 } 166 if (mMenu == null) { 167 mMenu = new PhotoMenu(mActivity, this, mPieRenderer); 168 mMenu.setListener(listener); 169 } 170 mMenu.initialize(prefGroup); 171 172 if (mZoomRenderer == null) { 173 mZoomRenderer = new ZoomRenderer(mActivity); 174 mRenderOverlay.addRenderer(mZoomRenderer); 175 } 176 if (mGestures == null) { 177 // this will handle gesture disambiguation and dispatching 178 mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer); 179 } 180 mGestures.clearTouchReceivers(); 181 mGestures.setRenderOverlay(mRenderOverlay); 182 mGestures.addTouchReceiver(mMenuButton); 183 mGestures.addTouchReceiver(mBlocker); 184 // make sure to add touch targets for image capture 185 if (mController.isImageCaptureIntent()) { 186 if (mReviewCancelButton != null) { 187 mGestures.addTouchReceiver(mReviewCancelButton); 188 } 189 if (mReviewDoneButton != null) { 190 mGestures.addTouchReceiver(mReviewDoneButton); 191 } 192 } 193 mRenderOverlay.requestLayout(); 194 195 initializeZoom(params); 196 updateOnScreenIndicators(params, prefs); 197 } 198 199 public void initializeControlByIntent() { 200 mBlocker = mActivity.findViewById(R.id.blocker); 201 mMenuButton = mActivity.findViewById(R.id.menu); 202 mMenuButton.setOnClickListener(new OnClickListener() { 203 @Override 204 public void onClick(View v) { 205 if (mPieRenderer != null) { 206 // If autofocus is not finished, cancel autofocus so that the 207 // subsequent touch can be handled by PreviewGestures 208 if (mController.getCameraState() == PhotoController.FOCUSING) { 209 mController.cancelAutoFocus(); 210 } 211 mPieRenderer.showInCenter(); 212 } 213 } 214 }); 215 if (mController.isImageCaptureIntent()) { 216 mActivity.hideSwitcher(); 217 ViewGroup cameraControls = (ViewGroup) mActivity.findViewById(R.id.camera_controls); 218 mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls); 219 220 mReviewDoneButton = mActivity.findViewById(R.id.btn_done); 221 mReviewCancelButton = mActivity.findViewById(R.id.btn_cancel); 222 mReviewRetakeButton = mActivity.findViewById(R.id.btn_retake); 223 mReviewCancelButton.setVisibility(View.VISIBLE); 224 225 mReviewDoneButton.setOnClickListener(new OnClickListener() { 226 @Override 227 public void onClick(View v) { 228 mController.onCaptureDone(); 229 } 230 }); 231 mReviewCancelButton.setOnClickListener(new OnClickListener() { 232 @Override 233 public void onClick(View v) { 234 mController.onCaptureCancelled(); 235 } 236 }); 237 238 mReviewRetakeButton.setOnClickListener(new OnClickListener() { 239 @Override 240 public void onClick(View v) { 241 mController.onCaptureRetake(); 242 } 243 }); 244 } 245 } 246 247 // called from onResume but only the first time 248 public void initializeFirstTime() { 249 // Initialize shutter button. 250 mShutterButton = mActivity.getShutterButton(); 251 mShutterButton.setImageResource(R.drawable.btn_new_shutter); 252 mShutterButton.setOnShutterButtonListener(mController); 253 mShutterButton.setVisibility(View.VISIBLE); 254 } 255 256 // called from onResume every other time 257 public void initializeSecondTime(Camera.Parameters params) { 258 initializeZoom(params); 259 if (mController.isImageCaptureIntent()) { 260 hidePostCaptureAlert(); 261 } 262 if (mMenu != null) { 263 mMenu.reloadPreferences(); 264 } 265 } 266 267 public void initializeZoom(Camera.Parameters params) { 268 if ((params == null) || !params.isZoomSupported() 269 || (mZoomRenderer == null)) return; 270 mZoomMax = params.getMaxZoom(); 271 mZoomRatios = params.getZoomRatios(); 272 // Currently we use immediate zoom for fast zooming to get better UX and 273 // there is no plan to take advantage of the smooth zoom. 274 if (mZoomRenderer != null) { 275 mZoomRenderer.setZoomMax(mZoomMax); 276 mZoomRenderer.setZoom(params.getZoom()); 277 mZoomRenderer.setZoomValue(mZoomRatios.get(params.getZoom())); 278 mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener()); 279 } 280 } 281 282 public void enableGestures(boolean enable) { 283 if (mGestures != null) { 284 mGestures.setEnabled(enable); 285 } 286 } 287 288 public void showGpsOnScreenIndicator(boolean hasSignal) { } 289 290 public void hideGpsOnScreenIndicator() { } 291 292 public void overrideSettings(final String ... keyvalues) { 293 mMenu.overrideSettings(keyvalues); 294 } 295 296 public void updateOnScreenIndicators(Camera.Parameters params, 297 ComboPreferences prefs) { 298 if (params == null) return; 299 updateSceneOnScreenIndicator(params.getSceneMode()); 300 updateExposureOnScreenIndicator(params, 301 CameraSettings.readExposure(prefs)); 302 updateFlashOnScreenIndicator(params.getFlashMode()); 303 updateHdrOnScreenIndicator(params.getSceneMode()); 304 } 305 306 private void updateExposureOnScreenIndicator(Camera.Parameters params, int value) { 307 if (mExposureIndicator == null) { 308 return; 309 } 310 int id = 0; 311 float step = params.getExposureCompensationStep(); 312 value = (int) Math.round(value * step); 313 switch(value) { 314 case -3: 315 id = R.drawable.ic_indicator_ev_n3; 316 break; 317 case -2: 318 id = R.drawable.ic_indicator_ev_n2; 319 break; 320 case -1: 321 id = R.drawable.ic_indicator_ev_n1; 322 break; 323 case 0: 324 id = R.drawable.ic_indicator_ev_0; 325 break; 326 case 1: 327 id = R.drawable.ic_indicator_ev_p1; 328 break; 329 case 2: 330 id = R.drawable.ic_indicator_ev_p2; 331 break; 332 case 3: 333 id = R.drawable.ic_indicator_ev_p3; 334 break; 335 } 336 mExposureIndicator.setImageResource(id); 337 } 338 339 private void updateFlashOnScreenIndicator(String value) { 340 if (mFlashIndicator == null) { 341 return; 342 } 343 if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) { 344 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off); 345 } else { 346 if (Parameters.FLASH_MODE_AUTO.equals(value)) { 347 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_auto); 348 } else if (Parameters.FLASH_MODE_ON.equals(value)) { 349 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_on); 350 } else { 351 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off); 352 } 353 } 354 } 355 356 private void updateSceneOnScreenIndicator(String value) { 357 if (mSceneIndicator == null) { 358 return; 359 } 360 if ((value == null) || Parameters.SCENE_MODE_AUTO.equals(value) 361 || Parameters.SCENE_MODE_HDR.equals(value)) { 362 mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_off); 363 } else { 364 mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_on); 365 } 366 } 367 368 private void updateHdrOnScreenIndicator(String value) { 369 if (mHdrIndicator == null) { 370 return; 371 } 372 if ((value != null) && Parameters.SCENE_MODE_HDR.equals(value)) { 373 mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_on); 374 } else { 375 mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_off); 376 } 377 } 378 379 public void setCameraState(int state) { 380 } 381 382 public boolean dispatchTouchEvent(MotionEvent m) { 383 if (mPopup != null) { 384 return mActivity.superDispatchTouchEvent(m); 385 } else if (mGestures != null && mRenderOverlay != null) { 386 return mGestures.dispatchTouch(m); 387 } 388 return false; 389 } 390 391 public boolean onBackPressed() { 392 if (mPieRenderer != null && mPieRenderer.showsItems()) { 393 mPieRenderer.hide(); 394 return true; 395 } 396 // In image capture mode, back button should: 397 // 1) if there is any popup, dismiss them, 2) otherwise, get out of 398 // image capture 399 if (mController.isImageCaptureIntent()) { 400 if (!removeTopLevelPopup()) { 401 // no popup to dismiss, cancel image capture 402 mController.onCaptureCancelled(); 403 } 404 return true; 405 } else if (!mController.isCameraIdle()) { 406 // ignore backs while we're taking a picture 407 return true; 408 } else { 409 return removeTopLevelPopup(); 410 } 411 } 412 413 public void onFullScreenChanged(boolean full) { 414 if (mFaceView != null) { 415 mFaceView.setBlockDraw(!full); 416 } 417 if (mPopup != null) { 418 dismissPopup(false, full); 419 } 420 if (mGestures != null) { 421 mGestures.setEnabled(full); 422 } 423 if (mRenderOverlay != null) { 424 // this can not happen in capture mode 425 mRenderOverlay.setVisibility(full ? View.VISIBLE : View.GONE); 426 } 427 if (mPieRenderer != null) { 428 mPieRenderer.setBlockFocus(!full); 429 } 430 setShowMenu(full); 431 if (mBlocker != null) { 432 mBlocker.setVisibility(full ? View.VISIBLE : View.GONE); 433 } 434 if (!full && mCountDownView != null) mCountDownView.cancelCountDown(); 435 } 436 437 public boolean removeTopLevelPopup() { 438 // Remove the top level popup or dialog box and return true if there's any 439 if (mPopup != null) { 440 dismissPopup(true); 441 return true; 442 } 443 return false; 444 } 445 446 public void showPopup(AbstractSettingPopup popup) { 447 mActivity.hideUI(); 448 mBlocker.setVisibility(View.INVISIBLE); 449 setShowMenu(false); 450 mPopup = popup; 451 mPopup.setVisibility(View.VISIBLE); 452 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 453 LayoutParams.WRAP_CONTENT); 454 lp.gravity = Gravity.CENTER; 455 ((FrameLayout) mRootView).addView(mPopup, lp); 456 } 457 458 public void dismissPopup(boolean topPopupOnly) { 459 dismissPopup(topPopupOnly, true); 460 } 461 462 private void dismissPopup(boolean topOnly, boolean fullScreen) { 463 if (fullScreen) { 464 mActivity.showUI(); 465 mBlocker.setVisibility(View.VISIBLE); 466 } 467 setShowMenu(fullScreen); 468 if (mPopup != null) { 469 ((FrameLayout) mRootView).removeView(mPopup); 470 mPopup = null; 471 } 472 mMenu.popupDismissed(topOnly); 473 } 474 475 public void onShowSwitcherPopup() { 476 if (mPieRenderer != null && mPieRenderer.showsItems()) { 477 mPieRenderer.hide(); 478 } 479 } 480 481 private void setShowMenu(boolean show) { 482 if (mOnScreenIndicators != null) { 483 mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE); 484 } 485 if (mMenuButton != null) { 486 mMenuButton.setVisibility(show ? View.VISIBLE : View.GONE); 487 } 488 } 489 490 public boolean collapseCameraControls() { 491 // Remove all the popups/dialog boxes 492 boolean ret = false; 493 if (mPopup != null) { 494 dismissPopup(false); 495 ret = true; 496 } 497 return ret; 498 } 499 500 protected void showPostCaptureAlert() { 501 mOnScreenIndicators.setVisibility(View.GONE); 502 mMenuButton.setVisibility(View.GONE); 503 Util.fadeIn(mReviewDoneButton); 504 mShutterButton.setVisibility(View.INVISIBLE); 505 Util.fadeIn(mReviewRetakeButton); 506 } 507 508 protected void hidePostCaptureAlert() { 509 mOnScreenIndicators.setVisibility(View.VISIBLE); 510 mMenuButton.setVisibility(View.VISIBLE); 511 Util.fadeOut(mReviewDoneButton); 512 mShutterButton.setVisibility(View.VISIBLE); 513 Util.fadeOut(mReviewRetakeButton); 514 } 515 516 public void setDisplayOrientation(int orientation) { 517 if (mFaceView != null) { 518 mFaceView.setDisplayOrientation(orientation); 519 } 520 } 521 522 // shutter button handling 523 524 public boolean isShutterPressed() { 525 return mShutterButton.isPressed(); 526 } 527 528 // focus handling 529 530 531 private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener { 532 @Override 533 public void onZoomValueChanged(int index) { 534 int newZoom = mController.onZoomChanged(index); 535 if (mZoomRenderer != null) { 536 mZoomRenderer.setZoomValue(mZoomRatios.get(newZoom)); 537 } 538 } 539 540 @Override 541 public void onZoomStart() { 542 if (mPieRenderer != null) { 543 mPieRenderer.setBlockFocus(true); 544 } 545 } 546 547 @Override 548 public void onZoomEnd() { 549 if (mPieRenderer != null) { 550 mPieRenderer.setBlockFocus(false); 551 } 552 } 553 } 554 555 @Override 556 public void onPieOpened(int centerX, int centerY) { 557 mActivity.cancelActivityTouchHandling(); 558 mActivity.setSwipingEnabled(false); 559 if (mFaceView != null) { 560 mFaceView.setBlockDraw(true); 561 } 562 } 563 564 @Override 565 public void onPieClosed() { 566 mActivity.setSwipingEnabled(true); 567 if (mFaceView != null) { 568 mFaceView.setBlockDraw(false); 569 } 570 } 571 572 // Surface Listener 573 574 @Override 575 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 576 Log.v(TAG, "surfaceChanged:" + holder + " width=" + width + ". height=" 577 + height); 578 } 579 580 @Override 581 public void surfaceCreated(SurfaceHolder holder) { 582 Log.v(TAG, "surfaceCreated: " + holder); 583 mSurfaceHolder = holder; 584 mController.onSurfaceCreated(holder); 585 } 586 587 @Override 588 public void surfaceDestroyed(SurfaceHolder holder) { 589 Log.v(TAG, "surfaceDestroyed: " + holder); 590 mSurfaceHolder = null; 591 mController.stopPreview(); 592 } 593 594 public Object getSurfaceTexture() { 595 return mSurfaceTexture; 596 } 597 598 public void setSurfaceTexture(Object st) { 599 mSurfaceTexture = st; 600 } 601 602 public SurfaceHolder getSurfaceHolder() { 603 return mSurfaceHolder; 604 } 605 606 public boolean isCountingDown() { 607 return mCountDownView.isCountingDown(); 608 } 609 610 public void cancelCountDown() { 611 mCountDownView.cancelCountDown(); 612 } 613 614 public void startCountDown(int sec, boolean playSound) { 615 mCountDownView.startCountDown(sec, playSound); 616 } 617 618 public void showPreferencesToast() { 619 if (mNotSelectableToast == null) { 620 String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode); 621 mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT); 622 } 623 mNotSelectableToast.show(); 624 } 625 626 public void onPause() { 627 mCountDownView.cancelCountDown(); 628 // Close the camera now because other activities may need to use it. 629 mSurfaceTexture = null; 630 631 // Clear UI. 632 collapseCameraControls(); 633 if (mFaceView != null) mFaceView.clear(); 634 635 636 mRootView.removeOnLayoutChangeListener(mLayoutListener); 637 mPreviewWidth = 0; 638 mPreviewHeight = 0; 639 } 640 641 public void enableShutter(boolean enabled) { 642 if (mShutterButton != null) { 643 mShutterButton.setEnabled(enabled); 644 } 645 } 646 647 public void pressShutterButton() { 648 if (mShutterButton.isInTouchMode()) { 649 mShutterButton.requestFocusFromTouch(); 650 } else { 651 mShutterButton.requestFocus(); 652 } 653 mShutterButton.setPressed(true); 654 } 655 656 // forward from preview gestures to controller 657 @Override 658 public void onSingleTapUp(View view, int x, int y) { 659 mController.onSingleTapUp(view, x, y); 660 } 661 662 // focus UI implementation 663 664 private FocusIndicator getFocusIndicator() { 665 return (mFaceView != null && mFaceView.faceExists()) ? mFaceView : mPieRenderer; 666 } 667 668 @Override 669 public boolean hasFaces() { 670 return (mFaceView != null && mFaceView.faceExists()); 671 } 672 673 public void clearFaces() { 674 if (mFaceView != null) mFaceView.clear(); 675 } 676 677 @Override 678 public void clearFocus() { 679 FocusIndicator indicator = getFocusIndicator(); 680 if (indicator != null) indicator.clear(); 681 } 682 683 @Override 684 public void setFocusPosition(int x, int y) { 685 mPieRenderer.setFocus(x, y); 686 } 687 688 @Override 689 public void onFocusStarted() { 690 getFocusIndicator().showStart(); 691 } 692 693 @Override 694 public void onFocusSucceeded(boolean timeout) { 695 getFocusIndicator().showSuccess(timeout); 696 } 697 698 @Override 699 public void onFocusFailed(boolean timeout) { 700 getFocusIndicator().showFail(timeout); 701 } 702 703 @Override 704 public void pauseFaceDetection() { 705 if (mFaceView != null) mFaceView.pause(); 706 } 707 708 @Override 709 public void resumeFaceDetection() { 710 if (mFaceView != null) mFaceView.resume(); 711 } 712 713 public void onStartFaceDetection(int orientation, boolean mirror) { 714 mFaceView.clear(); 715 mFaceView.setVisibility(View.VISIBLE); 716 mFaceView.setDisplayOrientation(orientation); 717 mFaceView.setMirror(mirror); 718 mFaceView.resume(); 719 } 720 721 @Override 722 public void onFaceDetection(Face[] faces, android.hardware.Camera camera) { 723 mFaceView.setFaces(faces); 724 } 725 726} 727