CameraAppUI.java revision 971ad50c0f220a396ffce50245617ff59ed0a09e
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 17package com.android.camera.app; 18 19import android.content.res.Resources; 20import android.graphics.Bitmap; 21import android.graphics.Canvas; 22import android.graphics.Matrix; 23import android.graphics.RectF; 24import android.graphics.SurfaceTexture; 25import android.hardware.display.DisplayManager; 26import android.util.CameraPerformanceTracker; 27import android.view.GestureDetector; 28import android.view.LayoutInflater; 29import android.view.MotionEvent; 30import android.view.TextureView; 31import android.view.View; 32import android.view.ViewConfiguration; 33import android.view.ViewGroup; 34import android.widget.FrameLayout; 35 36import com.android.camera.AccessibilityUtil; 37import com.android.camera.AnimationManager; 38import com.android.camera.ButtonManager; 39import com.android.camera.CaptureLayoutHelper; 40import com.android.camera.ShutterButton; 41import com.android.camera.TextureViewHelper; 42import com.android.camera.debug.Log; 43import com.android.camera.filmstrip.FilmstripContentPanel; 44import com.android.camera.hardware.HardwareSpec; 45import com.android.camera.module.ModuleController; 46import com.android.camera.settings.Keys; 47import com.android.camera.settings.SettingsManager; 48import com.android.camera.ui.AbstractTutorialOverlay; 49import com.android.camera.ui.BottomBar; 50import com.android.camera.ui.CaptureAnimationOverlay; 51import com.android.camera.ui.GridLines; 52import com.android.camera.ui.MainActivityLayout; 53import com.android.camera.ui.ModeListView; 54import com.android.camera.ui.ModeTransitionView; 55import com.android.camera.ui.PreviewOverlay; 56import com.android.camera.ui.PreviewStatusListener; 57import com.android.camera.ui.StickyBottomCaptureLayout; 58import com.android.camera.ui.TouchCoordinate; 59import com.android.camera.ui.focus.FocusRing; 60import com.android.camera.util.AndroidServices; 61import com.android.camera.util.ApiHelper; 62import com.android.camera.util.CameraUtil; 63import com.android.camera.util.Gusterpolator; 64import com.android.camera.util.PhotoSphereHelper; 65import com.android.camera.widget.Cling; 66import com.android.camera.widget.FilmstripLayout; 67import com.android.camera.widget.IndicatorIconController; 68import com.android.camera.widget.ModeOptionsOverlay; 69import com.android.camera.widget.RoundedThumbnailView; 70import com.android.camera2.R; 71 72/** 73 * CameraAppUI centralizes control of views shared across modules. Whereas module 74 * specific views will be handled in each Module UI. For example, we can now 75 * bring the flash animation and capture animation up from each module to app 76 * level, as these animations are largely the same for all modules. 77 * 78 * This class also serves to disambiguate touch events. It recognizes all the 79 * swipe gestures that happen on the preview by attaching a touch listener to 80 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge 81 * of how swipe from each direction should be handled, it can then redirect these 82 * events to appropriate recipient views. 83 */ 84public class CameraAppUI implements ModeListView.ModeSwitchListener, 85 TextureView.SurfaceTextureListener, 86 ModeListView.ModeListOpenListener, 87 SettingsManager.OnSettingChangedListener, 88 ShutterButton.OnShutterButtonListener { 89 90 /** 91 * The bottom controls on the filmstrip. 92 */ 93 public static interface BottomPanel { 94 /** Values for the view state of the button. */ 95 public final int VIEWER_NONE = 0; 96 public final int VIEWER_PHOTO_SPHERE = 1; 97 public final int VIEWER_REFOCUS = 2; 98 public final int VIEWER_OTHER = 3; 99 100 /** 101 * Sets a new or replaces an existing listener for bottom control events. 102 */ 103 void setListener(Listener listener); 104 105 /** 106 * Sets cling for external viewer button. 107 */ 108 void setClingForViewer(int viewerType, Cling cling); 109 110 /** 111 * Clears cling for external viewer button. 112 */ 113 void clearClingForViewer(int viewerType); 114 115 /** 116 * Returns a cling for the specified viewer type. 117 */ 118 Cling getClingForViewer(int viewerType); 119 120 /** 121 * Set if the bottom controls are visible. 122 * @param visible {@code true} if visible. 123 */ 124 void setVisible(boolean visible); 125 126 /** 127 * @param visible Whether the button is visible. 128 */ 129 void setEditButtonVisibility(boolean visible); 130 131 /** 132 * @param enabled Whether the button is enabled. 133 */ 134 void setEditEnabled(boolean enabled); 135 136 /** 137 * Sets the visibility of the view-photosphere button. 138 * 139 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE}, 140 * {@link #VIEWER_REFOCUS}. 141 */ 142 void setViewerButtonVisibility(int state); 143 144 /** 145 * @param enabled Whether the button is enabled. 146 */ 147 void setViewEnabled(boolean enabled); 148 149 /** 150 * @param enabled Whether the button is enabled. 151 */ 152 void setTinyPlanetEnabled(boolean enabled); 153 154 /** 155 * @param visible Whether the button is visible. 156 */ 157 void setDeleteButtonVisibility(boolean visible); 158 159 /** 160 * @param enabled Whether the button is enabled. 161 */ 162 void setDeleteEnabled(boolean enabled); 163 164 /** 165 * @param visible Whether the button is visible. 166 */ 167 void setShareButtonVisibility(boolean visible); 168 169 /** 170 * @param enabled Whether the button is enabled. 171 */ 172 void setShareEnabled(boolean enabled); 173 174 /** 175 * Sets the texts for progress UI. 176 * 177 * @param text The text to show. 178 */ 179 void setProgressText(CharSequence text); 180 181 /** 182 * Sets the progress. 183 * 184 * @param progress The progress value. Should be between 0 and 100. 185 */ 186 void setProgress(int progress); 187 188 /** 189 * Replaces the progress UI with an error message. 190 */ 191 void showProgressError(CharSequence message); 192 193 /** 194 * Hide the progress error message. 195 */ 196 void hideProgressError(); 197 198 /** 199 * Shows the progress. 200 */ 201 void showProgress(); 202 203 /** 204 * Hides the progress. 205 */ 206 void hideProgress(); 207 208 /** 209 * Shows the controls. 210 */ 211 void showControls(); 212 213 /** 214 * Hides the controls. 215 */ 216 void hideControls(); 217 218 /** 219 * Classes implementing this interface can listen for events on the bottom 220 * controls. 221 */ 222 public static interface Listener { 223 /** 224 * Called when the user pressed the "view" button to e.g. view a photo 225 * sphere or RGBZ image. 226 */ 227 public void onExternalViewer(); 228 229 /** 230 * Called when the "edit" button is pressed. 231 */ 232 public void onEdit(); 233 234 /** 235 * Called when the "tiny planet" button is pressed. 236 */ 237 public void onTinyPlanet(); 238 239 /** 240 * Called when the "delete" button is pressed. 241 */ 242 public void onDelete(); 243 244 /** 245 * Called when the "share" button is pressed. 246 */ 247 public void onShare(); 248 249 /** 250 * Called when the progress error message is clicked. 251 */ 252 public void onProgressErrorClicked(); 253 } 254 } 255 256 /** 257 * BottomBarUISpec provides a structure for modules 258 * to specify their ideal bottom bar mode options layout. 259 * 260 * Once constructed by a module, this class should be 261 * treated as read only. 262 * 263 * The application then edits this spec according to 264 * hardware limitations and displays the final bottom 265 * bar ui. 266 */ 267 public static class BottomBarUISpec { 268 /** Mode options UI */ 269 270 /** 271 * Set true if the camera option should be enabled. 272 * If not set or false, and multiple cameras are supported, 273 * the camera option will be disabled. 274 * 275 * If multiple cameras are not supported, this preference 276 * is ignored and the camera option will not be visible. 277 */ 278 public boolean enableCamera; 279 280 /** 281 * Set true if the camera option should not be visible, regardless 282 * of hardware limitations. 283 */ 284 public boolean hideCamera; 285 286 /** 287 * Set true if the photo flash option should be enabled. 288 * If not set or false, the photo flash option will be 289 * disabled. 290 * 291 * If the hardware does not support multiple flash values, 292 * this preference is ignored and the flash option will 293 * be disabled. It will not be made invisible in order to 294 * preserve a consistent experience across devices and between 295 * front and back cameras. 296 */ 297 public boolean enableFlash; 298 299 /** 300 * Set true if the video flash option should be enabled. 301 * Same disable rules apply as the photo flash option. 302 */ 303 public boolean enableTorchFlash; 304 305 /** 306 * Set true if the HDR+ flash option should be enabled. 307 * Same disable rules apply as the photo flash option. 308 */ 309 public boolean enableHdrPlusFlash; 310 311 /** 312 * Set true if flash should not be visible, regardless of 313 * hardware limitations. 314 */ 315 public boolean hideFlash; 316 317 /** 318 * Set true if the hdr/hdr+ option should be enabled. 319 * If not set or false, the hdr/hdr+ option will be disabled. 320 * 321 * Hdr or hdr+ will be chosen based on hardware limitations, 322 * with hdr+ prefered. 323 * 324 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+ 325 * will not be visible. 326 */ 327 public boolean enableHdr; 328 329 /** 330 * Set true if hdr/hdr+ should not be visible, regardless of 331 * hardware limitations. 332 */ 333 public boolean hideHdr; 334 335 /** 336 * Set true if grid lines should be visible. Not setting this 337 * causes grid lines to be disabled. This option is agnostic to 338 * the hardware. 339 */ 340 public boolean enableGridLines; 341 342 /** 343 * Set true if grid lines should not be visible. 344 */ 345 public boolean hideGridLines; 346 347 /** 348 * Set true if the panorama orientation option should be visible. 349 * 350 * This option is not constrained by hardware limitations. 351 */ 352 public boolean enablePanoOrientation; 353 354 public boolean enableExposureCompensation; 355 356 /** Intent UI */ 357 358 /** 359 * Set true if the intent ui cancel option should be visible. 360 */ 361 public boolean showCancel; 362 /** 363 * Set true if the intent ui done option should be visible. 364 */ 365 public boolean showDone; 366 /** 367 * Set true if the intent ui retake option should be visible. 368 */ 369 public boolean showRetake; 370 /** 371 * Set true if the intent ui review option should be visible. 372 */ 373 public boolean showReview; 374 375 /** Mode options callbacks */ 376 377 /** 378 * A {@link com.android.camera.ButtonManager.ButtonCallback} 379 * that will be executed when the camera option is pressed. This 380 * callback can be null. 381 */ 382 public ButtonManager.ButtonCallback cameraCallback; 383 384 /** 385 * A {@link com.android.camera.ButtonManager.ButtonCallback} 386 * that will be executed when the flash option is pressed. This 387 * callback can be null. 388 */ 389 public ButtonManager.ButtonCallback flashCallback; 390 391 /** 392 * A {@link com.android.camera.ButtonManager.ButtonCallback} 393 * that will be executed when the hdr/hdr+ option is pressed. This 394 * callback can be null. 395 */ 396 public ButtonManager.ButtonCallback hdrCallback; 397 398 /** 399 * A {@link com.android.camera.ButtonManager.ButtonCallback} 400 * that will be executed when the grid lines option is pressed. This 401 * callback can be null. 402 */ 403 public ButtonManager.ButtonCallback gridLinesCallback; 404 405 /** 406 * A {@link com.android.camera.ButtonManager.ButtonCallback} 407 * that will execute when the panorama orientation option is pressed. 408 * This callback can be null. 409 */ 410 public ButtonManager.ButtonCallback panoOrientationCallback; 411 412 /** Intent UI callbacks */ 413 414 /** 415 * A {@link android.view.View.OnClickListener} that will execute 416 * when the cancel option is pressed. This callback can be null. 417 */ 418 public View.OnClickListener cancelCallback; 419 420 /** 421 * A {@link android.view.View.OnClickListener} that will execute 422 * when the done option is pressed. This callback can be null. 423 */ 424 public View.OnClickListener doneCallback; 425 426 /** 427 * A {@link android.view.View.OnClickListener} that will execute 428 * when the retake option is pressed. This callback can be null. 429 */ 430 public View.OnClickListener retakeCallback; 431 432 /** 433 * A {@link android.view.View.OnClickListener} that will execute 434 * when the review option is pressed. This callback can be null. 435 */ 436 public View.OnClickListener reviewCallback; 437 438 /** 439 * A ExposureCompensationSetCallback that will execute 440 * when an expsosure button is pressed. This callback can be null. 441 */ 442 public interface ExposureCompensationSetCallback { 443 public void setExposure(int value); 444 } 445 public ExposureCompensationSetCallback exposureCompensationSetCallback; 446 447 /** 448 * Exposure compensation parameters. 449 */ 450 public int minExposureCompensation; 451 public int maxExposureCompensation; 452 public float exposureCompensationStep; 453 454 /** 455 * Whether self-timer is enabled. 456 */ 457 public boolean enableSelfTimer = false; 458 459 /** 460 * Whether the option for self-timer should show. If true and 461 * {@link #enableSelfTimer} is false, then the option should be shown 462 * disabled. 463 */ 464 public boolean showSelfTimer = false; 465 } 466 467 468 private final static Log.Tag TAG = new Log.Tag("CameraAppUI"); 469 470 private final AppController mController; 471 private final boolean mIsCaptureIntent; 472 private final AnimationManager mAnimationManager; 473 474 // Swipe states: 475 private final static int IDLE = 0; 476 private final static int SWIPE_UP = 1; 477 private final static int SWIPE_DOWN = 2; 478 private final static int SWIPE_LEFT = 3; 479 private final static int SWIPE_RIGHT = 4; 480 private boolean mSwipeEnabled = true; 481 482 // Shared Surface Texture properities. 483 private SurfaceTexture mSurface; 484 private int mSurfaceWidth; 485 private int mSurfaceHeight; 486 487 // Touch related measures: 488 private final int mSlop; 489 private final static int SWIPE_TIME_OUT_MS = 500; 490 491 // Mode cover states: 492 private final static int COVER_HIDDEN = 0; 493 private final static int COVER_SHOWN = 1; 494 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2; 495 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3; 496 497 /** 498 * Preview down-sample rate when taking a screenshot. 499 */ 500 private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2; 501 502 // App level views: 503 private final FrameLayout mCameraRootView; 504 private final ModeTransitionView mModeTransitionView; 505 private final MainActivityLayout mAppRootView; 506 private final ModeListView mModeListView; 507 private final FilmstripLayout mFilmstripLayout; 508 private TextureView mTextureView; 509 private FrameLayout mModuleUI; 510 private ShutterButton mShutterButton; 511 private BottomBar mBottomBar; 512 private ModeOptionsOverlay mModeOptionsOverlay; 513 private IndicatorIconController mIndicatorIconController; 514 private FocusRing mFocusRing; 515 private FrameLayout mTutorialsPlaceHolderWrapper; 516 private StickyBottomCaptureLayout mStickyBottomCaptureLayout; 517 private TextureViewHelper mTextureViewHelper; 518 private final GestureDetector mGestureDetector; 519 private DisplayManager.DisplayListener mDisplayListener; 520 private int mLastRotation; 521 private int mSwipeState = IDLE; 522 private PreviewOverlay mPreviewOverlay; 523 private GridLines mGridLines; 524 private CaptureAnimationOverlay mCaptureOverlay; 525 private PreviewStatusListener mPreviewStatusListener; 526 private int mModeCoverState = COVER_HIDDEN; 527 private final FilmstripBottomPanel mFilmstripBottomControls; 528 private final FilmstripContentPanel mFilmstripPanel; 529 private Runnable mHideCoverRunnable; 530 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener 531 = new View.OnLayoutChangeListener() { 532 @Override 533 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 534 int oldTop, int oldRight, int oldBottom) { 535 if (mPreviewStatusListener != null) { 536 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft, 537 oldTop, oldRight, oldBottom); 538 } 539 } 540 }; 541 private View mModeOptionsToggle; 542 private final RoundedThumbnailView mRoundedThumbnailView; 543 private final CaptureLayoutHelper mCaptureLayoutHelper; 544 private final View mAccessibilityAffordances; 545 private AccessibilityUtil mAccessibilityUtil; 546 547 private boolean mDisableAllUserInteractions; 548 /** Whether to prevent capture indicator from being triggered. */ 549 private boolean mSuppressCaptureIndicator; 550 551 /** 552 * Provides current preview frame and the controls/overlay from the module that 553 * are shown on top of the preview. 554 */ 555 public interface CameraModuleScreenShotProvider { 556 /** 557 * Returns the current preview frame down-sampled using the given down-sample 558 * factor. 559 * 560 * @param downSampleFactor the down sample factor for down sampling the 561 * preview frame. (e.g. a down sample factor of 562 * 2 means to scale down the preview frame to 1/2 563 * the width and height.) 564 * @return down-sampled preview frame 565 */ 566 public Bitmap getPreviewFrame(int downSampleFactor); 567 568 /** 569 * @return the controls and overlays that are currently showing on top of 570 * the preview drawn into a bitmap with no scaling applied. 571 */ 572 public Bitmap getPreviewOverlayAndControls(); 573 574 /** 575 * Returns a bitmap containing the current screenshot. 576 * 577 * @param previewDownSampleFactor the downsample factor applied on the 578 * preview frame when taking the screenshot 579 */ 580 public Bitmap getScreenShot(int previewDownSampleFactor); 581 } 582 583 /** 584 * This listener gets called when the size of the window (excluding the system 585 * decor such as status bar and nav bar) has changed. 586 */ 587 public interface NonDecorWindowSizeChangedListener { 588 public void onNonDecorWindowSizeChanged(int width, int height, int rotation); 589 } 590 591 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider = 592 new CameraModuleScreenShotProvider() { 593 @Override 594 public Bitmap getPreviewFrame(int downSampleFactor) { 595 if (mCameraRootView == null || mTextureView == null) { 596 return null; 597 } 598 // Gets the bitmap from the preview TextureView. 599 Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor); 600 return preview; 601 } 602 603 @Override 604 public Bitmap getPreviewOverlayAndControls() { 605 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(), 606 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888); 607 Canvas canvas = new Canvas(overlays); 608 mCameraRootView.draw(canvas); 609 return overlays; 610 } 611 612 @Override 613 public Bitmap getScreenShot(int previewDownSampleFactor) { 614 Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(), 615 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888); 616 Canvas canvas = new Canvas(screenshot); 617 canvas.drawARGB(255, 0, 0, 0); 618 Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor); 619 if (preview != null) { 620 canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null); 621 } 622 Bitmap overlay = getPreviewOverlayAndControls(); 623 if (overlay != null) { 624 canvas.drawBitmap(overlay, 0f, 0f, null); 625 } 626 return screenshot; 627 } 628 }; 629 630 private long mCoverHiddenTime = -1; // System time when preview cover was hidden. 631 632 public long getCoverHiddenTime() { 633 return mCoverHiddenTime; 634 } 635 636 /** 637 * This resets the preview to have no applied transform matrix. 638 */ 639 public void clearPreviewTransform() { 640 mTextureViewHelper.clearTransform(); 641 } 642 643 public void updatePreviewAspectRatio(float aspectRatio) { 644 mTextureViewHelper.updateAspectRatio(aspectRatio); 645 } 646 647 /** 648 * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of 649 * its TextureView. This is necessary to get the expected behavior for the TextureView's 650 * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the 651 * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer 652 * size). 653 * 654 * b/17286155 - Tracking a fix for this in HardwareLayer. 655 */ 656 public void setDefaultBufferSizeToViewDimens() { 657 if (mSurface == null || mTextureView == null) { 658 Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup"); 659 return; 660 } 661 mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight()); 662 } 663 664 /** 665 * Updates the preview matrix without altering it. 666 * 667 * @param matrix 668 * @param aspectRatio the desired aspect ratio for the preview. 669 */ 670 public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) { 671 mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio); 672 } 673 674 /** 675 * @return the rect that will display the preview. 676 */ 677 public RectF getFullscreenRect() { 678 return mTextureViewHelper.getFullscreenRect(); 679 } 680 681 /** 682 * This is to support modules that calculate their own transform matrix because 683 * they need to use a transform matrix to rotate the preview. 684 * 685 * @param matrix transform matrix to be set on the TextureView 686 */ 687 public void updatePreviewTransform(Matrix matrix) { 688 mTextureViewHelper.updateTransform(matrix); 689 } 690 691 public interface AnimationFinishedListener { 692 public void onAnimationFinished(boolean success); 693 } 694 695 private class MyTouchListener implements View.OnTouchListener { 696 private boolean mScaleStarted = false; 697 @Override 698 public boolean onTouch(View v, MotionEvent event) { 699 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 700 mScaleStarted = false; 701 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { 702 mScaleStarted = true; 703 } 704 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event); 705 } 706 } 707 708 /** 709 * This gesture listener finds out the direction of the scroll gestures and 710 * sends them to CameraAppUI to do further handling. 711 */ 712 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener { 713 private MotionEvent mDown; 714 715 @Override 716 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) { 717 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS 718 || mSwipeState != IDLE 719 || mIsCaptureIntent 720 || !mSwipeEnabled) { 721 return false; 722 } 723 724 int deltaX = (int) (ev.getX() - mDown.getX()); 725 int deltaY = (int) (ev.getY() - mDown.getY()); 726 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { 727 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) { 728 // Calculate the direction of the swipe. 729 if (deltaX >= Math.abs(deltaY)) { 730 // Swipe right. 731 setSwipeState(SWIPE_RIGHT); 732 } else if (deltaX <= -Math.abs(deltaY)) { 733 // Swipe left. 734 setSwipeState(SWIPE_LEFT); 735 } 736 } 737 } 738 return true; 739 } 740 741 private void setSwipeState(int swipeState) { 742 mSwipeState = swipeState; 743 // Notify new swipe detected. 744 onSwipeDetected(swipeState); 745 } 746 747 @Override 748 public boolean onDown(MotionEvent ev) { 749 mDown = MotionEvent.obtain(ev); 750 mSwipeState = IDLE; 751 return false; 752 } 753 } 754 755 public CameraAppUI(AppController controller, MainActivityLayout appRootView, 756 boolean isCaptureIntent) { 757 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop(); 758 mController = controller; 759 mIsCaptureIntent = isCaptureIntent; 760 761 mAppRootView = appRootView; 762 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout); 763 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root); 764 mModeTransitionView = (ModeTransitionView) 765 mAppRootView.findViewById(R.id.mode_transition_view); 766 mFilmstripBottomControls = new FilmstripBottomPanel(controller, 767 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel)); 768 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout); 769 mGestureDetector = new GestureDetector(controller.getAndroidContext(), 770 new MyGestureListener()); 771 Resources res = controller.getAndroidContext().getResources(); 772 mCaptureLayoutHelper = new CaptureLayoutHelper( 773 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min), 774 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max), 775 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal)); 776 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout); 777 if (mModeListView != null) { 778 mModeListView.setModeSwitchListener(this); 779 mModeListView.setModeListOpenListener(this); 780 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider); 781 mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper); 782 boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean( 783 SettingsManager.SCOPE_GLOBAL, 784 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING); 785 mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling); 786 } else { 787 Log.e(TAG, "Cannot find mode list in the view hierarchy"); 788 } 789 mAnimationManager = new AnimationManager(); 790 mRoundedThumbnailView = (RoundedThumbnailView) appRootView.findViewById(R.id.rounded_thumbnail_view); 791 mRoundedThumbnailView.setCallback(new RoundedThumbnailView.Callback() { 792 @Override 793 public void onHitStateFinished() { 794 mFilmstripLayout.showFilmstrip(); 795 } 796 }); 797 798 mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper); 799 initDisplayListener(); 800 mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances); 801 View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button); 802 modeListToggle.setOnClickListener(new View.OnClickListener() { 803 @Override 804 public void onClick(View view) { 805 openModeList(); 806 } 807 }); 808 View filmstripToggle = mAppRootView.findViewById( 809 R.id.accessibility_filmstrip_toggle_button); 810 filmstripToggle.setOnClickListener(new View.OnClickListener() { 811 @Override 812 public void onClick(View view) { 813 showFilmstrip(); 814 } 815 }); 816 817 mSuppressCaptureIndicator = false; 818 } 819 820 821 /** 822 * Freeze what is currently shown on screen until the next preview frame comes 823 * in. 824 */ 825 public void freezeScreenUntilPreviewReady() { 826 Log.v(TAG, "freezeScreenUntilPreviewReady"); 827 mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider 828 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT)); 829 mHideCoverRunnable = new Runnable() { 830 @Override 831 public void run() { 832 mModeTransitionView.hideImageCover(); 833 } 834 }; 835 mModeCoverState = COVER_SHOWN; 836 } 837 838 /** 839 * Creates a cling for the specific viewer and links the cling to the corresponding 840 * button for layout position. 841 * 842 * @param viewerType defines which viewer the cling is for. 843 */ 844 public void setupClingForViewer(int viewerType) { 845 if (viewerType == BottomPanel.VIEWER_REFOCUS) { 846 FrameLayout filmstripContent = (FrameLayout) mAppRootView 847 .findViewById(R.id.camera_filmstrip_content_layout); 848 if (filmstripContent != null) { 849 // Creates refocus cling. 850 LayoutInflater inflater = AndroidServices.instance().provideLayoutInflater(); 851 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false); 852 // Sets instruction text in the cling. 853 refocusCling.setText(mController.getAndroidContext().getResources() 854 .getString(R.string.cling_text_for_refocus_editor_button)); 855 856 // Adds cling into view hierarchy. 857 int clingWidth = mController.getAndroidContext() 858 .getResources().getDimensionPixelSize(R.dimen.default_cling_width); 859 filmstripContent.addView(refocusCling, clingWidth, 860 ViewGroup.LayoutParams.WRAP_CONTENT); 861 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling); 862 } 863 } 864 } 865 866 /** 867 * Clears the listeners for the cling and remove it from the view hierarchy. 868 * 869 * @param viewerType defines which viewer the cling is for. 870 */ 871 public void clearClingForViewer(int viewerType) { 872 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType); 873 if (clingToBeRemoved == null) { 874 // No cling is created for the specific viewer type. 875 return; 876 } 877 mFilmstripBottomControls.clearClingForViewer(viewerType); 878 clingToBeRemoved.setVisibility(View.GONE); 879 mAppRootView.removeView(clingToBeRemoved); 880 } 881 882 /** 883 * Enable or disable swipe gestures. We want to disable them e.g. while we 884 * record a video. 885 */ 886 public void setSwipeEnabled(boolean enabled) { 887 mSwipeEnabled = enabled; 888 // TODO: This can be removed once we come up with a new design for handling swipe 889 // on shutter button and mode options. (More details: b/13751653) 890 mAppRootView.setSwipeEnabled(enabled); 891 } 892 893 public void onDestroy() { 894 AndroidServices.instance().provideDisplayManager() 895 .unregisterDisplayListener(mDisplayListener); 896 } 897 898 /** 899 * Initializes the display listener to listen to display changes such as 900 * 180-degree rotation change, which will not have an onConfigurationChanged 901 * callback. 902 */ 903 private void initDisplayListener() { 904 if (ApiHelper.HAS_DISPLAY_LISTENER) { 905 mLastRotation = CameraUtil.getDisplayRotation(); 906 907 mDisplayListener = new DisplayManager.DisplayListener() { 908 @Override 909 public void onDisplayAdded(int arg0) { 910 // Do nothing. 911 } 912 913 @Override 914 public void onDisplayChanged(int displayId) { 915 int rotation = CameraUtil.getDisplayRotation( 916 ); 917 if ((rotation - mLastRotation + 360) % 360 == 180 918 && mPreviewStatusListener != null) { 919 mPreviewStatusListener.onPreviewFlipped(); 920 mStickyBottomCaptureLayout.requestLayout(); 921 mModeListView.requestLayout(); 922 mTextureView.requestLayout(); 923 } 924 mLastRotation = rotation; 925 } 926 927 @Override 928 public void onDisplayRemoved(int arg0) { 929 // Do nothing. 930 } 931 }; 932 933 AndroidServices.instance().provideDisplayManager() 934 .registerDisplayListener(mDisplayListener, null); 935 } 936 } 937 938 /** 939 * Redirects touch events to appropriate recipient views based on swipe direction. 940 * More specifically, swipe up and swipe down will be handled by the view that handles 941 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected 942 * to mode list in order to bring up mode list. 943 */ 944 private void onSwipeDetected(int swipeState) { 945 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) { 946 // TODO: Polish quick switch after this release. 947 // Quick switch between modes. 948 int currentModuleIndex = mController.getCurrentModuleIndex(); 949 final int moduleToTransitionTo = 950 mController.getQuickSwitchToModuleId(currentModuleIndex); 951 if (currentModuleIndex != moduleToTransitionTo) { 952 mAppRootView.redirectTouchEventsTo(mModeTransitionView); 953 int shadeColorId = R.color.camera_gray_background; 954 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo, 955 mController.getAndroidContext()); 956 957 AnimationFinishedListener listener = new AnimationFinishedListener() { 958 @Override 959 public void onAnimationFinished(boolean success) { 960 if (success) { 961 mHideCoverRunnable = new Runnable() { 962 @Override 963 public void run() { 964 mModeTransitionView.startPeepHoleAnimation(); 965 } 966 }; 967 mModeCoverState = COVER_SHOWN; 968 // Go to new module when the previous operation is successful. 969 mController.onModeSelected(moduleToTransitionTo); 970 } 971 } 972 }; 973 } 974 } else if (swipeState == SWIPE_LEFT) { 975 // Pass the touch sequence to filmstrip layout. 976 mAppRootView.redirectTouchEventsTo(mFilmstripLayout); 977 } else if (swipeState == SWIPE_RIGHT) { 978 // Pass the touch to mode switcher 979 mAppRootView.redirectTouchEventsTo(mModeListView); 980 } 981 } 982 983 /** 984 * Gets called when activity resumes in preview. 985 */ 986 public void resume() { 987 // Show mode theme cover until preview is ready 988 showModeCoverUntilPreviewReady(); 989 990 // Hide action bar first since we are in full screen mode first, and 991 // switch the system UI to lights-out mode. 992 mFilmstripPanel.hide(); 993 994 // Show UI that is meant to only be used when spoken feedback is 995 // enabled. 996 mAccessibilityAffordances.setVisibility( 997 (!mIsCaptureIntent && mAccessibilityUtil.isAccessibilityEnabled()) ? View.VISIBLE 998 : View.GONE); 999 } 1000 1001 /** 1002 * Opens the mode list (e.g. because of the menu button being pressed) and 1003 * adapts the rest of the UI. 1004 */ 1005 public void openModeList() { 1006 mModeOptionsOverlay.closeModeOptions(); 1007 mModeListView.onMenuPressed(); 1008 } 1009 1010 public void showAccessibilityZoomUI(float maxZoom) { 1011 mAccessibilityUtil.showZoomUI(maxZoom); 1012 } 1013 1014 public void hideAccessibilityZoomUI() { 1015 mAccessibilityUtil.hideZoomUI(); 1016 } 1017 1018 /** 1019 * A cover view showing the mode theme color and mode icon will be visible on 1020 * top of preview until preview is ready (i.e. camera preview is started and 1021 * the first frame has been received). 1022 */ 1023 private void showModeCoverUntilPreviewReady() { 1024 int modeId = mController.getCurrentModuleIndex(); 1025 int colorId = R.color.camera_gray_background;; 1026 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext()); 1027 mModeTransitionView.setupModeCover(colorId, iconId); 1028 mHideCoverRunnable = new Runnable() { 1029 @Override 1030 public void run() { 1031 mModeTransitionView.hideModeCover(null); 1032 if (!mDisableAllUserInteractions) { 1033 showShimmyDelayed(); 1034 } 1035 } 1036 }; 1037 mModeCoverState = COVER_SHOWN; 1038 } 1039 1040 private void showShimmyDelayed() { 1041 if (!mIsCaptureIntent) { 1042 // Show shimmy in SHIMMY_DELAY_MS 1043 mModeListView.showModeSwitcherHint(); 1044 } 1045 } 1046 1047 private void hideModeCover() { 1048 if (mHideCoverRunnable != null) { 1049 mAppRootView.post(mHideCoverRunnable); 1050 mHideCoverRunnable = null; 1051 } 1052 mModeCoverState = COVER_HIDDEN; 1053 if (mCoverHiddenTime < 0) { 1054 mCoverHiddenTime = System.currentTimeMillis(); 1055 } 1056 } 1057 1058 1059 public void onPreviewVisiblityChanged(int visibility) { 1060 if (visibility == ModuleController.VISIBILITY_HIDDEN) { 1061 setIndicatorBottomBarWrapperVisible(false); 1062 mAccessibilityAffordances.setVisibility(View.GONE); 1063 } else { 1064 setIndicatorBottomBarWrapperVisible(true); 1065 if (!mIsCaptureIntent && mAccessibilityUtil.isAccessibilityEnabled()) { 1066 mAccessibilityAffordances.setVisibility(View.VISIBLE); 1067 } else { 1068 mAccessibilityAffordances.setVisibility(View.GONE); 1069 } 1070 } 1071 } 1072 1073 /** 1074 * Call to stop the preview from being rendered. Sets the entire capture 1075 * root view to invisible which includes the preview plus focus indicator 1076 * and any other auxiliary views for capture modes. 1077 */ 1078 public void pausePreviewRendering() { 1079 mCameraRootView.setVisibility(View.INVISIBLE); 1080 } 1081 1082 /** 1083 * Call to begin rendering the preview and auxiliary views again. 1084 */ 1085 public void resumePreviewRendering() { 1086 mCameraRootView.setVisibility(View.VISIBLE); 1087 } 1088 1089 /** 1090 * Returns the transform associated with the preview view. 1091 * 1092 * @param m the Matrix in which to copy the current transform. 1093 * @return The specified matrix if not null or a new Matrix instance 1094 * otherwise. 1095 */ 1096 public Matrix getPreviewTransform(Matrix m) { 1097 return mTextureView.getTransform(m); 1098 } 1099 1100 @Override 1101 public void onOpenFullScreen() { 1102 // Do nothing. 1103 } 1104 1105 @Override 1106 public void onModeListOpenProgress(float progress) { 1107 progress = 1 - progress; 1108 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress); 1109 mModeOptionsToggle.setAlpha(interpolatedProgress); 1110 // Change shutter button alpha linearly based on the mode list open progress: 1111 // set the alpha to disabled alpha when list is fully open, to enabled alpha 1112 // when the list is fully closed. 1113 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED 1114 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED); 1115 } 1116 1117 @Override 1118 public void onModeListClosed() { 1119 // Make sure the alpha on mode options ellipse is reset when mode drawer 1120 // is closed. 1121 mModeOptionsToggle.setAlpha(1f); 1122 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED); 1123 } 1124 1125 /** 1126 * Called when the back key is pressed. 1127 * 1128 * @return Whether the UI responded to the key event. 1129 */ 1130 public boolean onBackPressed() { 1131 if (mFilmstripLayout.getVisibility() == View.VISIBLE) { 1132 return mFilmstripLayout.onBackPressed(); 1133 } else { 1134 return mModeListView.onBackPressed(); 1135 } 1136 } 1137 1138 /** 1139 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that 1140 * listens to SurfaceTexture changes. In addition, listeners are set on 1141 * dependent app ui elements. 1142 * 1143 * @param previewStatusListener the listener that gets notified when SurfaceTexture 1144 * changes 1145 */ 1146 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) { 1147 mPreviewStatusListener = previewStatusListener; 1148 if (mPreviewStatusListener != null) { 1149 onPreviewListenerChanged(); 1150 } 1151 } 1152 1153 /** 1154 * When the PreviewStatusListener changes, listeners need to be 1155 * set on the following app ui elements: 1156 * {@link com.android.camera.ui.PreviewOverlay}, 1157 * {@link com.android.camera.ui.BottomBar}, 1158 * {@link com.android.camera.ui.IndicatorIconController}. 1159 */ 1160 private void onPreviewListenerChanged() { 1161 // Set a listener for recognizing preview gestures. 1162 GestureDetector.OnGestureListener gestureListener 1163 = mPreviewStatusListener.getGestureListener(); 1164 if (gestureListener != null) { 1165 mPreviewOverlay.setGestureListener(gestureListener); 1166 } 1167 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener(); 1168 if (touchListener != null) { 1169 mPreviewOverlay.setTouchListener(touchListener); 1170 } 1171 1172 mTextureViewHelper.setAutoAdjustTransform( 1173 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout()); 1174 } 1175 1176 /** 1177 * This method should be called in onCameraOpened. It defines CameraAppUI 1178 * specific changes that depend on the camera or camera settings. 1179 */ 1180 public void onChangeCamera() { 1181 ModuleController moduleController = mController.getCurrentModuleController(); 1182 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec()); 1183 1184 if (mIndicatorIconController != null) { 1185 // Sync the settings state with the indicator state. 1186 mIndicatorIconController.syncIndicators(); 1187 } 1188 } 1189 1190 /** 1191 * Adds a listener to receive callbacks when preview area changes. 1192 */ 1193 public void addPreviewAreaChangedListener( 1194 PreviewStatusListener.PreviewAreaChangedListener listener) { 1195 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener); 1196 } 1197 1198 /** 1199 * Removes a listener that receives callbacks when preview area changes. 1200 */ 1201 public void removePreviewAreaChangedListener( 1202 PreviewStatusListener.PreviewAreaChangedListener listener) { 1203 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener); 1204 } 1205 1206 /** 1207 * This inflates generic_module layout, which contains all the shared views across 1208 * modules. Then each module inflates their own views in the given view group. For 1209 * now, this is called every time switching from a not-yet-refactored module to a 1210 * refactored module. In the future, this should only need to be done once per app 1211 * start. 1212 */ 1213 public void prepareModuleUI() { 1214 mController.getSettingsManager().addListener(this); 1215 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout); 1216 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content); 1217 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper, 1218 mController.getCameraProvider()); 1219 mTextureViewHelper.setSurfaceTextureListener(this); 1220 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener); 1221 1222 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar); 1223 int unpressedColor = mController.getAndroidContext().getResources() 1224 .getColor(R.color.camera_gray_background); 1225 setBottomBarColor(unpressedColor); 1226 updateModeSpecificUIColors(); 1227 1228 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper); 1229 1230 mModeOptionsOverlay 1231 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay); 1232 1233 // Sets the visibility of the bottom bar and the mode options. 1234 resetBottomControls(mController.getCurrentModuleController(), 1235 mController.getCurrentModuleIndex()); 1236 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper); 1237 1238 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button); 1239 addShutterListener(mController.getCurrentModuleController()); 1240 addShutterListener(mModeOptionsOverlay); 1241 addShutterListener(this); 1242 1243 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines); 1244 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines); 1245 1246 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay); 1247 mPreviewOverlay.setOnTouchListener(new MyTouchListener()); 1248 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay); 1249 mAccessibilityUtil = new AccessibilityUtil(mPreviewOverlay, mAccessibilityAffordances); 1250 1251 mCaptureOverlay = (CaptureAnimationOverlay) 1252 mCameraRootView.findViewById(R.id.capture_overlay); 1253 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay); 1254 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay); 1255 1256 if (mIndicatorIconController == null) { 1257 mIndicatorIconController = 1258 new IndicatorIconController(mController, mAppRootView); 1259 } 1260 1261 mController.getButtonManager().load(mCameraRootView); 1262 mController.getButtonManager().setListener(mIndicatorIconController); 1263 mController.getSettingsManager().addListener(mIndicatorIconController); 1264 1265 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle); 1266 mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring); 1267 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView 1268 .findViewById(R.id.tutorials_placeholder_wrapper); 1269 mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView 1270 .findViewById(R.id.sticky_bottom_capture_layout); 1271 mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper); 1272 1273 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView); 1274 mTextureViewHelper.addAspectRatioChangedListener( 1275 new PreviewStatusListener.PreviewAspectRatioChangedListener() { 1276 @Override 1277 public void onPreviewAspectRatioChanged(float aspectRatio) { 1278 mModeOptionsOverlay.requestLayout(); 1279 mBottomBar.requestLayout(); 1280 } 1281 } 1282 ); 1283 } 1284 1285 /** 1286 * Called indirectly from each module in their initialization to get a view group 1287 * to inflate the module specific views in. 1288 * 1289 * @return a view group for modules to attach views to 1290 */ 1291 public FrameLayout getModuleRootView() { 1292 // TODO: Change it to mModuleUI when refactor is done 1293 return mCameraRootView; 1294 } 1295 1296 /** 1297 * Remove all the module specific views. 1298 */ 1299 public void clearModuleUI() { 1300 if (mModuleUI != null) { 1301 mModuleUI.removeAllViews(); 1302 } 1303 removeShutterListener(mController.getCurrentModuleController()); 1304 mTutorialsPlaceHolderWrapper.removeAllViews(); 1305 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE); 1306 1307 setShutterButtonEnabled(true); 1308 mPreviewStatusListener = null; 1309 mPreviewOverlay.reset(); 1310 1311 Log.v(TAG, "mFocusRing.stopFocusAnimations()"); 1312 mFocusRing.stopFocusAnimations(); 1313 } 1314 1315 /** 1316 * Gets called when preview is ready to start. It sets up one shot preview callback 1317 * in order to receive a callback when the preview frame is available, so that 1318 * the preview cover can be hidden to reveal preview. 1319 * 1320 * An alternative for getting the timing to hide preview cover is through 1321 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)}, 1322 * which is less accurate but therefore is the fallback for modules that manage 1323 * their own preview callbacks (as setting one preview callback will override 1324 * any other installed preview callbacks), or use camera2 API. 1325 */ 1326 public void onPreviewReadyToStart() { 1327 if (mModeCoverState == COVER_SHOWN) { 1328 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME; 1329 mController.setupOneShotPreviewListener(); 1330 } 1331 } 1332 1333 /** 1334 * Gets called when preview is started. 1335 */ 1336 public void onPreviewStarted() { 1337 Log.v(TAG, "onPreviewStarted"); 1338 if (mModeCoverState == COVER_SHOWN) { 1339 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE; 1340 } 1341 enableModeOptions(); 1342 } 1343 1344 /** 1345 * Gets notified when next preview frame comes in. 1346 */ 1347 public void onNewPreviewFrame() { 1348 Log.v(TAG, "onNewPreviewFrame"); 1349 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1350 hideModeCover(); 1351 } 1352 1353 @Override 1354 public void onShutterButtonClick() { 1355 /* 1356 * Set the mode options toggle unclickable, generally 1357 * throughout the app, whenever the shutter button is clicked. 1358 * 1359 * This could be done in the OnShutterButtonListener of the 1360 * ModeOptionsOverlay, but since it is very important that we 1361 * can clearly see when the toggle becomes clickable again, 1362 * keep all of that logic at this level. 1363 */ 1364 // disableModeOptions(); 1365 } 1366 1367 @Override 1368 public void onShutterCoordinate(TouchCoordinate coord) { 1369 // Do nothing. 1370 } 1371 1372 @Override 1373 public void onShutterButtonFocus(boolean pressed) { 1374 // noop 1375 } 1376 1377 @Override 1378 public void onShutterButtonLongPressed() { 1379 // noop 1380 } 1381 1382 /** 1383 * Set the mode options toggle clickable. 1384 */ 1385 public void enableModeOptions() { 1386 /* 1387 * For modules using camera 1 api, this gets called in 1388 * onSurfaceTextureUpdated whenever the preview gets stopped and 1389 * started after each capture. This also takes care of the 1390 * case where the mode options might be unclickable when we 1391 * switch modes 1392 * 1393 * For modules using camera 2 api, they're required to call this 1394 * method when a capture is "completed". Unfortunately this differs 1395 * per module implementation. 1396 */ 1397 if (!mDisableAllUserInteractions) { 1398 mModeOptionsOverlay.setToggleClickable(true); 1399 } 1400 } 1401 1402 /** 1403 * Set the mode options toggle not clickable. 1404 */ 1405 public void disableModeOptions() { 1406 mModeOptionsOverlay.setToggleClickable(false); 1407 } 1408 1409 public void setDisableAllUserInteractions(boolean disable) { 1410 if (disable) { 1411 disableModeOptions(); 1412 setShutterButtonEnabled(false); 1413 setSwipeEnabled(false); 1414 mModeListView.hideAnimated(); 1415 } else { 1416 enableModeOptions(); 1417 setShutterButtonEnabled(true); 1418 setSwipeEnabled(true); 1419 } 1420 mDisableAllUserInteractions = disable; 1421 } 1422 1423 @Override 1424 public void onModeButtonPressed(int modeIndex) { 1425 // TODO: Make CameraActivity listen to ModeListView's events. 1426 int pressedModuleId = mController.getModuleId(modeIndex); 1427 int currentModuleId = mController.getCurrentModuleIndex(); 1428 if (pressedModuleId != currentModuleId) { 1429 hideCaptureIndicator(); 1430 } 1431 } 1432 1433 /** 1434 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView} 1435 * 1436 * @param modeIndex mode index of the selected mode 1437 */ 1438 @Override 1439 public void onModeSelected(int modeIndex) { 1440 mHideCoverRunnable = new Runnable() { 1441 @Override 1442 public void run() { 1443 mModeListView.startModeSelectionAnimation(); 1444 } 1445 }; 1446 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED); 1447 mModeCoverState = COVER_SHOWN; 1448 1449 int lastIndex = mController.getCurrentModuleIndex(); 1450 // Actual mode teardown / new mode initialization happens here 1451 mController.onModeSelected(modeIndex); 1452 int currentIndex = mController.getCurrentModuleIndex(); 1453 1454 if (lastIndex == currentIndex) { 1455 hideModeCover(); 1456 } 1457 1458 updateModeSpecificUIColors(); 1459 } 1460 1461 private void updateModeSpecificUIColors() { 1462 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex()); 1463 } 1464 1465 @Override 1466 public void onSettingsSelected() { 1467 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL, 1468 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false); 1469 mModeListView.setShouldShowSettingsCling(false); 1470 mController.onSettingsSelected(); 1471 } 1472 1473 @Override 1474 public int getCurrentModeIndex() { 1475 return mController.getCurrentModuleIndex(); 1476 } 1477 1478 /********************** Capture animation **********************/ 1479 /* TODO: This session is subject to UX changes. In addition to the generic 1480 flash animation and post capture animation, consider designating a parameter 1481 for specifying the type of animation, as well as an animation finished listener 1482 so that modules can have more knowledge of the status of the animation. */ 1483 1484 /** 1485 * Turns on or off the capture indicator suppression. 1486 */ 1487 public void setShouldSuppressCaptureIndicator(boolean suppress) { 1488 mSuppressCaptureIndicator = suppress; 1489 } 1490 1491 /** 1492 * Starts the capture indicator pop-out animation. 1493 * 1494 * @param accessibilityString An accessibility String to be announced during the peek animation. 1495 */ 1496 public void startCaptureIndicatorRevealAnimation(String accessibilityString) { 1497 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) { 1498 return; 1499 } 1500 mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString); 1501 } 1502 1503 /** 1504 * Updates the thumbnail image in the capture indicator. 1505 * 1506 * @param thumbnailBitmap The thumbnail image to be shown. 1507 */ 1508 public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) { 1509 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) { 1510 return; 1511 } 1512 mRoundedThumbnailView.setThumbnail(thumbnailBitmap); 1513 mRoundedThumbnailView.setRotation(rotation); 1514 } 1515 1516 /** 1517 * Hides the capture indicator. 1518 */ 1519 public void hideCaptureIndicator() { 1520 mRoundedThumbnailView.hideThumbnail(); 1521 } 1522 1523 /** 1524 * Starts the flash animation. 1525 */ 1526 public void startFlashAnimation(boolean shortFlash) { 1527 mCaptureOverlay.startFlashAnimation(shortFlash); 1528 } 1529 1530 /** 1531 * Cancels the pre-capture animation. 1532 */ 1533 public void cancelPreCaptureAnimation() { 1534 mAnimationManager.cancelAnimations(); 1535 } 1536 1537 /** 1538 * Cancels the post-capture animation. 1539 */ 1540 public void cancelPostCaptureAnimation() { 1541 mAnimationManager.cancelAnimations(); 1542 } 1543 1544 public FilmstripContentPanel getFilmstripContentPanel() { 1545 return mFilmstripPanel; 1546 } 1547 1548 /** 1549 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the 1550 * bottom of the filmstrip. 1551 */ 1552 public BottomPanel getFilmstripBottomControls() { 1553 return mFilmstripBottomControls; 1554 } 1555 1556 public void showBottomControls() { 1557 mFilmstripBottomControls.show(); 1558 } 1559 1560 public void hideBottomControls() { 1561 mFilmstripBottomControls.hide(); 1562 } 1563 1564 /** 1565 * @param listener The listener for bottom controls. 1566 */ 1567 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) { 1568 mFilmstripBottomControls.setListener(listener); 1569 } 1570 1571 /***************************SurfaceTexture Api and Listener*********************************/ 1572 1573 /** 1574 * Return the shared surface texture. 1575 */ 1576 public SurfaceTexture getSurfaceTexture() { 1577 return mSurface; 1578 } 1579 1580 /** 1581 * Return the shared {@link android.graphics.SurfaceTexture}'s width. 1582 */ 1583 public int getSurfaceWidth() { 1584 return mSurfaceWidth; 1585 } 1586 1587 /** 1588 * Return the shared {@link android.graphics.SurfaceTexture}'s height. 1589 */ 1590 public int getSurfaceHeight() { 1591 return mSurfaceHeight; 1592 } 1593 1594 @Override 1595 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 1596 mSurface = surface; 1597 mSurfaceWidth = width; 1598 mSurfaceHeight = height; 1599 Log.v(TAG, "SurfaceTexture is available"); 1600 if (mPreviewStatusListener != null) { 1601 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height); 1602 } 1603 enableModeOptions(); 1604 } 1605 1606 @Override 1607 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 1608 mSurface = surface; 1609 mSurfaceWidth = width; 1610 mSurfaceHeight = height; 1611 if (mPreviewStatusListener != null) { 1612 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height); 1613 } 1614 } 1615 1616 @Override 1617 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 1618 mSurface = null; 1619 Log.v(TAG, "SurfaceTexture is destroyed"); 1620 if (mPreviewStatusListener != null) { 1621 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface); 1622 } 1623 return false; 1624 } 1625 1626 @Override 1627 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 1628 mSurface = surface; 1629 if (mPreviewStatusListener != null) { 1630 mPreviewStatusListener.onSurfaceTextureUpdated(surface); 1631 } 1632 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) { 1633 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated"); 1634 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1635 hideModeCover(); 1636 } 1637 } 1638 1639 /****************************Grid lines api ******************************/ 1640 1641 /** 1642 * Show a set of evenly spaced lines over the preview. The number 1643 * of lines horizontally and vertically is determined by 1644 * {@link com.android.camera.ui.GridLines}. 1645 */ 1646 public void showGridLines() { 1647 if (mGridLines != null) { 1648 mGridLines.setVisibility(View.VISIBLE); 1649 } 1650 } 1651 1652 /** 1653 * Hide the set of evenly spaced grid lines overlaying the preview. 1654 */ 1655 public void hideGridLines() { 1656 if (mGridLines != null) { 1657 mGridLines.setVisibility(View.INVISIBLE); 1658 } 1659 } 1660 1661 /** 1662 * Return a callback which shows or hide the preview grid lines 1663 * depending on whether the grid lines setting is set on. 1664 */ 1665 public ButtonManager.ButtonCallback getGridLinesCallback() { 1666 return new ButtonManager.ButtonCallback() { 1667 @Override 1668 public void onStateChanged(int state) { 1669 if (!mController.isPaused()) { 1670 if (Keys.areGridLinesOn(mController.getSettingsManager())) { 1671 showGridLines(); 1672 } else { 1673 hideGridLines(); 1674 } 1675 } 1676 } 1677 }; 1678 } 1679 1680 /***************************Mode options api *****************************/ 1681 1682 /** 1683 * Set the mode options visible. 1684 */ 1685 public void showModeOptions() { 1686 /* Make mode options clickable. */ 1687 enableModeOptions(); 1688 mModeOptionsOverlay.setVisibility(View.VISIBLE); 1689 } 1690 1691 /** 1692 * Set the mode options invisible. This is necessary for modes 1693 * that don't show a bottom bar for the capture UI. 1694 */ 1695 public void hideModeOptions() { 1696 mModeOptionsOverlay.setVisibility(View.INVISIBLE); 1697 } 1698 1699 /****************************Bottom bar api ******************************/ 1700 1701 /** 1702 * Sets up the bottom bar and mode options with the correct 1703 * shutter button and visibility based on the current module. 1704 */ 1705 public void resetBottomControls(ModuleController module, int moduleIndex) { 1706 if (areBottomControlsUsed(module)) { 1707 setBottomBarShutterIcon(moduleIndex); 1708 mCaptureLayoutHelper.setShowBottomBar(true); 1709 } else { 1710 mCaptureLayoutHelper.setShowBottomBar(false); 1711 } 1712 } 1713 1714 /** 1715 * Show or hide the mode options and bottom bar, based on 1716 * whether the current module is using the bottom bar. Returns 1717 * whether the mode options and bottom bar are used. 1718 */ 1719 private boolean areBottomControlsUsed(ModuleController module) { 1720 if (module.isUsingBottomBar()) { 1721 showBottomBar(); 1722 showModeOptions(); 1723 return true; 1724 } else { 1725 hideBottomBar(); 1726 hideModeOptions(); 1727 return false; 1728 } 1729 } 1730 1731 /** 1732 * Set the bottom bar visible. 1733 */ 1734 public void showBottomBar() { 1735 mBottomBar.setVisibility(View.VISIBLE); 1736 } 1737 1738 /** 1739 * Set the bottom bar invisible. 1740 */ 1741 public void hideBottomBar() { 1742 mBottomBar.setVisibility(View.INVISIBLE); 1743 } 1744 1745 /** 1746 * Sets the color of the bottom bar. 1747 */ 1748 public void setBottomBarColor(int colorId) { 1749 mBottomBar.setBackgroundColor(colorId); 1750 } 1751 1752 /** 1753 * Sets the pressed color of the bottom bar for a camera mode index. 1754 */ 1755 public void setBottomBarColorsForModeIndex(int index) { 1756 mBottomBar.setColorsForModeIndex(index); 1757 } 1758 1759 /** 1760 * Sets the shutter button icon on the bottom bar, based on 1761 * the mode index. 1762 */ 1763 public void setBottomBarShutterIcon(int modeIndex) { 1764 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex, 1765 mController.getAndroidContext()); 1766 mBottomBar.setShutterButtonIcon(shutterIconId); 1767 } 1768 1769 public void animateBottomBarToVideoStop(int shutterIconId) { 1770 mBottomBar.animateToVideoStop(shutterIconId); 1771 } 1772 1773 public void animateBottomBarToFullSize(int shutterIconId) { 1774 mBottomBar.animateToFullSize(shutterIconId); 1775 } 1776 1777 public void setShutterButtonEnabled(final boolean enabled) { 1778 if (!mDisableAllUserInteractions) { 1779 mBottomBar.post(new Runnable() { 1780 @Override 1781 public void run() { 1782 mBottomBar.setShutterButtonEnabled(enabled); 1783 } 1784 }); 1785 } 1786 } 1787 1788 public void setShutterButtonImportantToA11y(boolean important) { 1789 mBottomBar.setShutterButtonImportantToA11y(important); 1790 } 1791 1792 public boolean isShutterButtonEnabled() { 1793 return mBottomBar.isShutterButtonEnabled(); 1794 } 1795 1796 public void setIndicatorBottomBarWrapperVisible(boolean visible) { 1797 mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1798 } 1799 1800 /** 1801 * Set the visibility of the bottom bar. 1802 */ 1803 // TODO: needed for when panorama is managed by the generic module ui. 1804 public void setBottomBarVisible(boolean visible) { 1805 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1806 } 1807 1808 /** 1809 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button. 1810 */ 1811 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) { 1812 mShutterButton.addOnShutterButtonListener(listener); 1813 } 1814 1815 /** 1816 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button. 1817 */ 1818 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) { 1819 mShutterButton.removeOnShutterButtonListener(listener); 1820 } 1821 1822 /** 1823 * Performs a transition to the capture layout of the bottom bar. 1824 */ 1825 public void transitionToCapture() { 1826 ModuleController moduleController = mController.getCurrentModuleController(); 1827 applyModuleSpecs(moduleController.getHardwareSpec(), 1828 moduleController.getBottomBarSpec()); 1829 mBottomBar.transitionToCapture(); 1830 showModeOptions(); 1831 } 1832 1833 /** 1834 * Displays the Cancel button instead of the capture button. 1835 */ 1836 public void transitionToCancel() { 1837 ModuleController moduleController = mController.getCurrentModuleController(); 1838 applyModuleSpecs(moduleController.getHardwareSpec(), 1839 moduleController.getBottomBarSpec()); 1840 mBottomBar.transitionToCancel(); 1841 hideModeOptions(); 1842 } 1843 1844 /** 1845 * Performs a transition to the global intent layout. 1846 */ 1847 public void transitionToIntentCaptureLayout() { 1848 ModuleController moduleController = mController.getCurrentModuleController(); 1849 applyModuleSpecs(moduleController.getHardwareSpec(), 1850 moduleController.getBottomBarSpec()); 1851 mBottomBar.transitionToIntentCaptureLayout(); 1852 showModeOptions(); 1853 } 1854 1855 /** 1856 * Performs a transition to the global intent review layout. 1857 */ 1858 public void transitionToIntentReviewLayout() { 1859 ModuleController moduleController = mController.getCurrentModuleController(); 1860 applyModuleSpecs(moduleController.getHardwareSpec(), 1861 moduleController.getBottomBarSpec()); 1862 mBottomBar.transitionToIntentReviewLayout(); 1863 hideModeOptions(); 1864 } 1865 1866 /** 1867 * @return whether UI is in intent review mode 1868 */ 1869 public boolean isInIntentReview() { 1870 return mBottomBar.isInIntentReview(); 1871 } 1872 1873 @Override 1874 public void onSettingChanged(SettingsManager settingsManager, String key) { 1875 // Update the mode options based on the hardware spec, 1876 // when hdr changes to prevent flash from getting out of sync. 1877 if (key.equals(Keys.KEY_CAMERA_HDR)) { 1878 ModuleController moduleController = mController.getCurrentModuleController(); 1879 applyModuleSpecs(moduleController.getHardwareSpec(), 1880 moduleController.getBottomBarSpec()); 1881 } 1882 } 1883 1884 /** 1885 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec} 1886 * to the bottom bar mode options based on limitations from a 1887 * {@link com.android.camera.hardware.HardwareSpec}. 1888 * 1889 * Options not supported by the hardware are either hidden 1890 * or disabled, depending on the option. 1891 * 1892 * Otherwise, the option is fully enabled and clickable. 1893 */ 1894 public void applyModuleSpecs(final HardwareSpec hardwareSpec, 1895 final BottomBarUISpec bottomBarSpec) { 1896 if (hardwareSpec == null || bottomBarSpec == null) { 1897 return; 1898 } 1899 1900 ButtonManager buttonManager = mController.getButtonManager(); 1901 SettingsManager settingsManager = mController.getSettingsManager(); 1902 1903 buttonManager.setToInitialState(); 1904 1905 /** Standard mode options */ 1906 if (mController.getCameraProvider().getNumberOfCameras() > 1 && 1907 hardwareSpec.isFrontCameraSupported()) { 1908 if (bottomBarSpec.enableCamera) { 1909 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA, 1910 bottomBarSpec.cameraCallback); 1911 } else { 1912 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA); 1913 } 1914 } else { 1915 // Hide camera icon if front camera not available. 1916 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA); 1917 } 1918 1919 if (bottomBarSpec.hideFlash) { 1920 // Hide both flash and torch button in flash disable logic 1921 buttonManager.hideButton(ButtonManager.BUTTON_FLASH); 1922 buttonManager.hideButton(ButtonManager.BUTTON_TORCH); 1923 } else { 1924 if (hardwareSpec.isFlashSupported()) { 1925 if (bottomBarSpec.enableFlash) { 1926 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH, 1927 bottomBarSpec.flashCallback); 1928 } else if (bottomBarSpec.enableTorchFlash) { 1929 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH, 1930 bottomBarSpec.flashCallback); 1931 } else if (bottomBarSpec.enableHdrPlusFlash) { 1932 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH, 1933 bottomBarSpec.flashCallback); 1934 } else { 1935 // Hide both flash and torch button in flash disable logic 1936 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1937 buttonManager.disableButton(ButtonManager.BUTTON_TORCH); 1938 } 1939 } else { 1940 // Disable both flash and torch icon if not supported 1941 // by the chosen camera hardware. 1942 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1943 buttonManager.disableButton(ButtonManager.BUTTON_TORCH); 1944 } 1945 } 1946 1947 if (bottomBarSpec.hideHdr || mIsCaptureIntent) { 1948 // Force hide hdr or hdr plus icon. 1949 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS); 1950 } else { 1951 if (hardwareSpec.isHdrPlusSupported()) { 1952 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager, 1953 mController.getModuleScope())) { 1954 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS, 1955 bottomBarSpec.hdrCallback); 1956 } else { 1957 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS); 1958 } 1959 } else if (hardwareSpec.isHdrSupported()) { 1960 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager, 1961 mController.getModuleScope())) { 1962 buttonManager.initializeButton(ButtonManager.BUTTON_HDR, 1963 bottomBarSpec.hdrCallback); 1964 } else { 1965 buttonManager.disableButton(ButtonManager.BUTTON_HDR); 1966 } 1967 } else { 1968 // Hide hdr plus or hdr icon if neither are supported. 1969 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS); 1970 } 1971 } 1972 1973 if (bottomBarSpec.hideGridLines) { 1974 // Force hide grid lines icon. 1975 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES); 1976 hideGridLines(); 1977 } else { 1978 if (bottomBarSpec.enableGridLines) { 1979 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES, 1980 bottomBarSpec.gridLinesCallback != null ? 1981 bottomBarSpec.gridLinesCallback : getGridLinesCallback() 1982 ); 1983 } else { 1984 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES); 1985 hideGridLines(); 1986 } 1987 } 1988 1989 if (bottomBarSpec.enableSelfTimer) { 1990 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null); 1991 } else { 1992 if (bottomBarSpec.showSelfTimer) { 1993 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN); 1994 } else { 1995 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN); 1996 } 1997 } 1998 1999 if (bottomBarSpec.enablePanoOrientation 2000 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) { 2001 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback); 2002 } 2003 2004 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation && 2005 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) && 2006 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL, 2007 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED); 2008 if (enableExposureCompensation) { 2009 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, 2010 new View.OnClickListener() { 2011 @Override 2012 public void onClick(View v) { 2013 mModeOptionsOverlay.showExposureOptions(); 2014 } 2015 }); 2016 buttonManager.setExposureCompensationParameters( 2017 bottomBarSpec.minExposureCompensation, 2018 bottomBarSpec.maxExposureCompensation, 2019 bottomBarSpec.exposureCompensationStep); 2020 2021 buttonManager.setExposureCompensationCallback( 2022 bottomBarSpec.exposureCompensationSetCallback); 2023 buttonManager.updateExposureButtons(); 2024 } else { 2025 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION); 2026 buttonManager.setExposureCompensationCallback(null); 2027 } 2028 2029 /** Intent UI */ 2030 if (bottomBarSpec.showCancel) { 2031 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL, 2032 bottomBarSpec.cancelCallback); 2033 } 2034 if (bottomBarSpec.showDone) { 2035 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE, 2036 bottomBarSpec.doneCallback); 2037 } 2038 if (bottomBarSpec.showRetake) { 2039 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE, 2040 bottomBarSpec.retakeCallback, 2041 R.drawable.ic_back, 2042 R.string.retake_button_description); 2043 } 2044 if (bottomBarSpec.showReview) { 2045 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW, 2046 bottomBarSpec.reviewCallback, 2047 R.drawable.ic_play, 2048 R.string.review_button_description); 2049 } 2050 } 2051 2052 /** 2053 * Shows the given tutorial on the screen. 2054 */ 2055 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) { 2056 tutorial.show(mTutorialsPlaceHolderWrapper, inflater); 2057 } 2058 2059 /***************************Filmstrip api *****************************/ 2060 2061 public void showFilmstrip() { 2062 mModeListView.onBackPressed(); 2063 mFilmstripLayout.showFilmstrip(); 2064 } 2065 2066 public void hideFilmstrip() { 2067 mFilmstripLayout.hideFilmstrip(); 2068 } 2069} 2070