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