CameraAppUI.java revision 86f1d1f3664c909d1d949951056c7bb1e9627324
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 1218 if (mIndicatorIconController != null) { 1219 // Sync the settings state with the indicator state. 1220 mIndicatorIconController.syncIndicators(); 1221 } 1222 } 1223 1224 /** 1225 * Adds a listener to receive callbacks when preview area changes. 1226 */ 1227 public void addPreviewAreaChangedListener( 1228 PreviewStatusListener.PreviewAreaChangedListener listener) { 1229 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener); 1230 } 1231 1232 /** 1233 * Removes a listener that receives callbacks when preview area changes. 1234 */ 1235 public void removePreviewAreaChangedListener( 1236 PreviewStatusListener.PreviewAreaChangedListener listener) { 1237 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener); 1238 } 1239 1240 /** 1241 * This inflates generic_module layout, which contains all the shared views across 1242 * modules. Then each module inflates their own views in the given view group. For 1243 * now, this is called every time switching from a not-yet-refactored module to a 1244 * refactored module. In the future, this should only need to be done once per app 1245 * start. 1246 */ 1247 public void prepareModuleUI() { 1248 mController.getSettingsManager().addListener(this); 1249 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout); 1250 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content); 1251 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper, 1252 mController.getCameraProvider()); 1253 mTextureViewHelper.setSurfaceTextureListener(this); 1254 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener); 1255 1256 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar); 1257 int unpressedColor = mController.getAndroidContext().getResources() 1258 .getColor(R.color.camera_gray_background); 1259 setBottomBarColor(unpressedColor); 1260 updateModeSpecificUIColors(); 1261 1262 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper); 1263 1264 mModeOptionsOverlay 1265 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay); 1266 1267 // Sets the visibility of the bottom bar and the mode options. 1268 resetBottomControls(mController.getCurrentModuleController(), 1269 mController.getCurrentModuleIndex()); 1270 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper); 1271 1272 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button); 1273 addShutterListener(mController.getCurrentModuleController()); 1274 addShutterListener(mModeOptionsOverlay); 1275 addShutterListener(this); 1276 1277 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines); 1278 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines); 1279 1280 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay); 1281 mPreviewOverlay.setOnTouchListener(new MyTouchListener()); 1282 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay); 1283 mAccessibilityUtil = new AccessibilityUtil(mPreviewOverlay, mAccessibilityAffordances); 1284 1285 mCaptureOverlay = (CaptureAnimationOverlay) 1286 mCameraRootView.findViewById(R.id.capture_overlay); 1287 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay); 1288 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay); 1289 1290 if (mIndicatorIconController == null) { 1291 mIndicatorIconController = 1292 new IndicatorIconController(mController, mAppRootView); 1293 } 1294 1295 mController.getButtonManager().load(mCameraRootView); 1296 mController.getButtonManager().setListener(mIndicatorIconController); 1297 mController.getSettingsManager().addListener(mIndicatorIconController); 1298 1299 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle); 1300 mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring); 1301 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView 1302 .findViewById(R.id.tutorials_placeholder_wrapper); 1303 mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView 1304 .findViewById(R.id.sticky_bottom_capture_layout); 1305 mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper); 1306 mCountdownCancelButton = (ImageButton) mStickyBottomCaptureLayout 1307 .findViewById(R.id.shutter_cancel_button); 1308 1309 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView); 1310 mTextureViewHelper.addAspectRatioChangedListener( 1311 new PreviewStatusListener.PreviewAspectRatioChangedListener() { 1312 @Override 1313 public void onPreviewAspectRatioChanged(float aspectRatio) { 1314 mModeOptionsOverlay.requestLayout(); 1315 mBottomBar.requestLayout(); 1316 } 1317 } 1318 ); 1319 } 1320 1321 /** 1322 * Called indirectly from each module in their initialization to get a view group 1323 * to inflate the module specific views in. 1324 * 1325 * @return a view group for modules to attach views to 1326 */ 1327 public FrameLayout getModuleRootView() { 1328 // TODO: Change it to mModuleUI when refactor is done 1329 return mCameraRootView; 1330 } 1331 1332 /** 1333 * Remove all the module specific views. 1334 */ 1335 public void clearModuleUI() { 1336 if (mModuleUI != null) { 1337 mModuleUI.removeAllViews(); 1338 } 1339 removeShutterListener(mController.getCurrentModuleController()); 1340 mTutorialsPlaceHolderWrapper.removeAllViews(); 1341 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE); 1342 1343 setShutterButtonEnabled(true); 1344 mPreviewStatusListener = null; 1345 mPreviewOverlay.reset(); 1346 1347 Log.v(TAG, "mFocusRing.stopFocusAnimations()"); 1348 mFocusRing.stopFocusAnimations(); 1349 } 1350 1351 /** 1352 * Gets called when preview is ready to start. It sets up one shot preview callback 1353 * in order to receive a callback when the preview frame is available, so that 1354 * the preview cover can be hidden to reveal preview. 1355 * 1356 * An alternative for getting the timing to hide preview cover is through 1357 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)}, 1358 * which is less accurate but therefore is the fallback for modules that manage 1359 * their own preview callbacks (as setting one preview callback will override 1360 * any other installed preview callbacks), or use camera2 API. 1361 */ 1362 public void onPreviewReadyToStart() { 1363 if (mModeCoverState == COVER_SHOWN) { 1364 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME; 1365 mController.setupOneShotPreviewListener(); 1366 } 1367 } 1368 1369 /** 1370 * Gets called when preview is started. 1371 */ 1372 public void onPreviewStarted() { 1373 Log.v(TAG, "onPreviewStarted"); 1374 if (mModeCoverState == COVER_SHOWN) { 1375 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE; 1376 } 1377 enableModeOptions(); 1378 } 1379 1380 /** 1381 * Gets notified when next preview frame comes in. 1382 */ 1383 public void onNewPreviewFrame() { 1384 Log.v(TAG, "onNewPreviewFrame"); 1385 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1386 hideModeCover(); 1387 } 1388 1389 @Override 1390 public void onShutterButtonClick() { 1391 /* 1392 * Set the mode options toggle unclickable, generally 1393 * throughout the app, whenever the shutter button is clicked. 1394 * 1395 * This could be done in the OnShutterButtonListener of the 1396 * ModeOptionsOverlay, but since it is very important that we 1397 * can clearly see when the toggle becomes clickable again, 1398 * keep all of that logic at this level. 1399 */ 1400 // disableModeOptions(); 1401 } 1402 1403 @Override 1404 public void onShutterCoordinate(TouchCoordinate coord) { 1405 // Do nothing. 1406 } 1407 1408 @Override 1409 public void onShutterButtonFocus(boolean pressed) { 1410 // noop 1411 } 1412 1413 @Override 1414 public void onShutterButtonLongPressed() { 1415 // noop 1416 } 1417 1418 /** 1419 * Set the mode options toggle clickable. 1420 */ 1421 public void enableModeOptions() { 1422 /* 1423 * For modules using camera 1 api, this gets called in 1424 * onSurfaceTextureUpdated whenever the preview gets stopped and 1425 * started after each capture. This also takes care of the 1426 * case where the mode options might be unclickable when we 1427 * switch modes 1428 * 1429 * For modules using camera 2 api, they're required to call this 1430 * method when a capture is "completed". Unfortunately this differs 1431 * per module implementation. 1432 */ 1433 if (!mDisableAllUserInteractions) { 1434 mModeOptionsOverlay.setToggleClickable(true); 1435 } 1436 } 1437 1438 /** 1439 * Set the mode options toggle not clickable. 1440 */ 1441 public void disableModeOptions() { 1442 mModeOptionsOverlay.setToggleClickable(false); 1443 } 1444 1445 public void setDisableAllUserInteractions(boolean disable) { 1446 if (disable) { 1447 disableModeOptions(); 1448 setShutterButtonEnabled(false); 1449 setSwipeEnabled(false); 1450 mModeListView.hideAnimated(); 1451 } else { 1452 enableModeOptions(); 1453 setShutterButtonEnabled(true); 1454 setSwipeEnabled(true); 1455 } 1456 mDisableAllUserInteractions = disable; 1457 } 1458 1459 @Override 1460 public void onModeButtonPressed(int modeIndex) { 1461 // TODO: Make CameraActivity listen to ModeListView's events. 1462 int pressedModuleId = mController.getModuleId(modeIndex); 1463 int currentModuleId = mController.getCurrentModuleIndex(); 1464 if (pressedModuleId != currentModuleId) { 1465 hideCaptureIndicator(); 1466 } 1467 } 1468 1469 /** 1470 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView} 1471 * 1472 * @param modeIndex mode index of the selected mode 1473 */ 1474 @Override 1475 public void onModeSelected(int modeIndex) { 1476 mHideCoverRunnable = new Runnable() { 1477 @Override 1478 public void run() { 1479 mModeListView.startModeSelectionAnimation(); 1480 } 1481 }; 1482 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED); 1483 mModeCoverState = COVER_SHOWN; 1484 1485 int lastIndex = mController.getCurrentModuleIndex(); 1486 // Actual mode teardown / new mode initialization happens here 1487 mController.onModeSelected(modeIndex); 1488 int currentIndex = mController.getCurrentModuleIndex(); 1489 1490 if (lastIndex == currentIndex) { 1491 hideModeCover(); 1492 } 1493 1494 updateModeSpecificUIColors(); 1495 } 1496 1497 private void updateModeSpecificUIColors() { 1498 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex()); 1499 } 1500 1501 @Override 1502 public void onSettingsSelected() { 1503 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL, 1504 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false); 1505 mModeListView.setShouldShowSettingsCling(false); 1506 mController.onSettingsSelected(); 1507 } 1508 1509 @Override 1510 public int getCurrentModeIndex() { 1511 return mController.getCurrentModuleIndex(); 1512 } 1513 1514 /********************** Capture animation **********************/ 1515 /* TODO: This session is subject to UX changes. In addition to the generic 1516 flash animation and post capture animation, consider designating a parameter 1517 for specifying the type of animation, as well as an animation finished listener 1518 so that modules can have more knowledge of the status of the animation. */ 1519 1520 /** 1521 * Turns on or off the capture indicator suppression. 1522 */ 1523 public void setShouldSuppressCaptureIndicator(boolean suppress) { 1524 mSuppressCaptureIndicator = suppress; 1525 } 1526 1527 /** 1528 * Starts the capture indicator pop-out animation. 1529 * 1530 * @param accessibilityString An accessibility String to be announced during the peek animation. 1531 */ 1532 public void startCaptureIndicatorRevealAnimation(String accessibilityString) { 1533 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) { 1534 return; 1535 } 1536 mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString); 1537 } 1538 1539 /** 1540 * Updates the thumbnail image in the capture indicator. 1541 * 1542 * @param thumbnailBitmap The thumbnail image to be shown. 1543 */ 1544 public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) { 1545 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) { 1546 return; 1547 } 1548 mRoundedThumbnailView.setThumbnail(thumbnailBitmap, rotation); 1549 } 1550 1551 /** 1552 * Hides the capture indicator. 1553 */ 1554 public void hideCaptureIndicator() { 1555 mRoundedThumbnailView.hideThumbnail(); 1556 } 1557 1558 /** 1559 * Starts the flash animation. 1560 */ 1561 public void startFlashAnimation(boolean shortFlash) { 1562 mCaptureOverlay.startFlashAnimation(shortFlash); 1563 } 1564 1565 /** 1566 * Cancels the pre-capture animation. 1567 */ 1568 public void cancelPreCaptureAnimation() { 1569 mAnimationManager.cancelAnimations(); 1570 } 1571 1572 /** 1573 * Cancels the post-capture animation. 1574 */ 1575 public void cancelPostCaptureAnimation() { 1576 mAnimationManager.cancelAnimations(); 1577 } 1578 1579 public FilmstripContentPanel getFilmstripContentPanel() { 1580 return mFilmstripPanel; 1581 } 1582 1583 /** 1584 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the 1585 * bottom of the filmstrip. 1586 */ 1587 public BottomPanel getFilmstripBottomControls() { 1588 return mFilmstripBottomControls; 1589 } 1590 1591 public void showBottomControls() { 1592 mFilmstripBottomControls.show(); 1593 } 1594 1595 public void hideBottomControls() { 1596 mFilmstripBottomControls.hide(); 1597 } 1598 1599 /** 1600 * @param listener The listener for bottom controls. 1601 */ 1602 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) { 1603 mFilmstripBottomControls.setListener(listener); 1604 } 1605 1606 /***************************SurfaceTexture Api and Listener*********************************/ 1607 1608 /** 1609 * Return the shared surface texture. 1610 */ 1611 public SurfaceTexture getSurfaceTexture() { 1612 return mSurface; 1613 } 1614 1615 /** 1616 * Return the shared {@link android.graphics.SurfaceTexture}'s width. 1617 */ 1618 public int getSurfaceWidth() { 1619 return mSurfaceWidth; 1620 } 1621 1622 /** 1623 * Return the shared {@link android.graphics.SurfaceTexture}'s height. 1624 */ 1625 public int getSurfaceHeight() { 1626 return mSurfaceHeight; 1627 } 1628 1629 @Override 1630 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 1631 mSurface = surface; 1632 mSurfaceWidth = width; 1633 mSurfaceHeight = height; 1634 Log.v(TAG, "SurfaceTexture is available"); 1635 if (mPreviewStatusListener != null) { 1636 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height); 1637 } 1638 enableModeOptions(); 1639 } 1640 1641 @Override 1642 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 1643 mSurface = surface; 1644 mSurfaceWidth = width; 1645 mSurfaceHeight = height; 1646 if (mPreviewStatusListener != null) { 1647 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height); 1648 } 1649 } 1650 1651 @Override 1652 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 1653 mSurface = null; 1654 Log.v(TAG, "SurfaceTexture is destroyed"); 1655 if (mPreviewStatusListener != null) { 1656 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface); 1657 } 1658 return false; 1659 } 1660 1661 @Override 1662 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 1663 mSurface = surface; 1664 if (mPreviewStatusListener != null) { 1665 mPreviewStatusListener.onSurfaceTextureUpdated(surface); 1666 } 1667 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) { 1668 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated"); 1669 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1670 hideModeCover(); 1671 } 1672 } 1673 1674 /****************************Grid lines api ******************************/ 1675 1676 /** 1677 * Show a set of evenly spaced lines over the preview. The number 1678 * of lines horizontally and vertically is determined by 1679 * {@link com.android.camera.ui.GridLines}. 1680 */ 1681 public void showGridLines() { 1682 if (mGridLines != null) { 1683 mGridLines.setVisibility(View.VISIBLE); 1684 } 1685 } 1686 1687 /** 1688 * Hide the set of evenly spaced grid lines overlaying the preview. 1689 */ 1690 public void hideGridLines() { 1691 if (mGridLines != null) { 1692 mGridLines.setVisibility(View.INVISIBLE); 1693 } 1694 } 1695 1696 /** 1697 * Return a callback which shows or hide the preview grid lines 1698 * depending on whether the grid lines setting is set on. 1699 */ 1700 public ButtonManager.ButtonCallback getGridLinesCallback() { 1701 return new ButtonManager.ButtonCallback() { 1702 @Override 1703 public void onStateChanged(int state) { 1704 if (!mController.isPaused()) { 1705 if (Keys.areGridLinesOn(mController.getSettingsManager())) { 1706 showGridLines(); 1707 } else { 1708 hideGridLines(); 1709 } 1710 } 1711 } 1712 }; 1713 } 1714 1715 /***************************Mode options api *****************************/ 1716 1717 /** 1718 * Set the mode options visible. 1719 */ 1720 public void showModeOptions() { 1721 /* Make mode options clickable. */ 1722 enableModeOptions(); 1723 mModeOptionsOverlay.setVisibility(View.VISIBLE); 1724 } 1725 1726 /** 1727 * Set the mode options invisible. This is necessary for modes 1728 * that don't show a bottom bar for the capture UI. 1729 */ 1730 public void hideModeOptions() { 1731 mModeOptionsOverlay.setVisibility(View.INVISIBLE); 1732 } 1733 1734 /****************************Bottom bar api ******************************/ 1735 1736 /** 1737 * Sets up the bottom bar and mode options with the correct 1738 * shutter button and visibility based on the current module. 1739 */ 1740 public void resetBottomControls(ModuleController module, int moduleIndex) { 1741 if (areBottomControlsUsed(module)) { 1742 setBottomBarShutterIcon(moduleIndex); 1743 mCaptureLayoutHelper.setShowBottomBar(true); 1744 } else { 1745 mCaptureLayoutHelper.setShowBottomBar(false); 1746 } 1747 } 1748 1749 /** 1750 * Show or hide the mode options and bottom bar, based on 1751 * whether the current module is using the bottom bar. Returns 1752 * whether the mode options and bottom bar are used. 1753 */ 1754 private boolean areBottomControlsUsed(ModuleController module) { 1755 if (module.isUsingBottomBar()) { 1756 showBottomBar(); 1757 showModeOptions(); 1758 return true; 1759 } else { 1760 hideBottomBar(); 1761 hideModeOptions(); 1762 return false; 1763 } 1764 } 1765 1766 /** 1767 * Set the bottom bar visible. 1768 */ 1769 public void showBottomBar() { 1770 mBottomBar.setVisibility(View.VISIBLE); 1771 } 1772 1773 /** 1774 * Set the bottom bar invisible. 1775 */ 1776 public void hideBottomBar() { 1777 mBottomBar.setVisibility(View.INVISIBLE); 1778 } 1779 1780 /** 1781 * Sets the color of the bottom bar. 1782 */ 1783 public void setBottomBarColor(int colorId) { 1784 mBottomBar.setBackgroundColor(colorId); 1785 } 1786 1787 /** 1788 * Sets the pressed color of the bottom bar for a camera mode index. 1789 */ 1790 public void setBottomBarColorsForModeIndex(int index) { 1791 mBottomBar.setColorsForModeIndex(index); 1792 } 1793 1794 /** 1795 * Sets the shutter button icon on the bottom bar, based on 1796 * the mode index. 1797 */ 1798 public void setBottomBarShutterIcon(int modeIndex) { 1799 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex, 1800 mController.getAndroidContext()); 1801 mBottomBar.setShutterButtonIcon(shutterIconId); 1802 } 1803 1804 public void animateBottomBarToVideoStop(int shutterIconId) { 1805 mBottomBar.animateToVideoStop(shutterIconId); 1806 } 1807 1808 public void animateBottomBarToFullSize(int shutterIconId) { 1809 mBottomBar.animateToFullSize(shutterIconId); 1810 } 1811 1812 public void setShutterButtonEnabled(final boolean enabled) { 1813 if (!mDisableAllUserInteractions) { 1814 mBottomBar.post(new Runnable() { 1815 @Override 1816 public void run() { 1817 mBottomBar.setShutterButtonEnabled(enabled); 1818 } 1819 }); 1820 } 1821 } 1822 1823 public void setShutterButtonImportantToA11y(boolean important) { 1824 mBottomBar.setShutterButtonImportantToA11y(important); 1825 } 1826 1827 public boolean isShutterButtonEnabled() { 1828 return mBottomBar.isShutterButtonEnabled(); 1829 } 1830 1831 public void setIndicatorBottomBarWrapperVisible(boolean visible) { 1832 mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1833 } 1834 1835 /** 1836 * Set the visibility of the bottom bar. 1837 */ 1838 // TODO: needed for when panorama is managed by the generic module ui. 1839 public void setBottomBarVisible(boolean visible) { 1840 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1841 } 1842 1843 /** 1844 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button. 1845 */ 1846 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) { 1847 mShutterButton.addOnShutterButtonListener(listener); 1848 } 1849 1850 /** 1851 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button. 1852 */ 1853 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) { 1854 mShutterButton.removeOnShutterButtonListener(listener); 1855 } 1856 1857 /** 1858 * Sets or replaces the "cancel shutter" button listener. 1859 * <p> 1860 * TODO: Make this part of the interface the same way shutter button 1861 * listeners are. 1862 */ 1863 public void setCancelShutterButtonListener(View.OnClickListener listener) { 1864 mCountdownCancelButton.setOnClickListener(listener); 1865 } 1866 1867 /** 1868 * Performs a transition to the capture layout of the bottom bar. 1869 */ 1870 public void transitionToCapture() { 1871 ModuleController moduleController = mController.getCurrentModuleController(); 1872 applyModuleSpecs(moduleController.getHardwareSpec(), 1873 moduleController.getBottomBarSpec()); 1874 mBottomBar.transitionToCapture(); 1875 showModeOptions(); 1876 } 1877 1878 /** 1879 * Displays the Cancel button instead of the capture button. 1880 */ 1881 public void transitionToCancel() { 1882 ModuleController moduleController = mController.getCurrentModuleController(); 1883 applyModuleSpecs(moduleController.getHardwareSpec(), 1884 moduleController.getBottomBarSpec()); 1885 mBottomBar.transitionToCancel(); 1886 hideModeOptions(); 1887 } 1888 1889 /** 1890 * Performs a transition to the global intent layout. 1891 */ 1892 public void transitionToIntentCaptureLayout() { 1893 ModuleController moduleController = mController.getCurrentModuleController(); 1894 applyModuleSpecs(moduleController.getHardwareSpec(), 1895 moduleController.getBottomBarSpec()); 1896 mBottomBar.transitionToIntentCaptureLayout(); 1897 showModeOptions(); 1898 } 1899 1900 /** 1901 * Performs a transition to the global intent review layout. 1902 */ 1903 public void transitionToIntentReviewLayout() { 1904 ModuleController moduleController = mController.getCurrentModuleController(); 1905 applyModuleSpecs(moduleController.getHardwareSpec(), 1906 moduleController.getBottomBarSpec()); 1907 mBottomBar.transitionToIntentReviewLayout(); 1908 hideModeOptions(); 1909 } 1910 1911 /** 1912 * @return whether UI is in intent review mode 1913 */ 1914 public boolean isInIntentReview() { 1915 return mBottomBar.isInIntentReview(); 1916 } 1917 1918 @Override 1919 public void onSettingChanged(SettingsManager settingsManager, String key) { 1920 // Update the mode options based on the hardware spec, 1921 // when hdr changes to prevent flash from getting out of sync. 1922 if (key.equals(Keys.KEY_CAMERA_HDR)) { 1923 ModuleController moduleController = mController.getCurrentModuleController(); 1924 applyModuleSpecs(moduleController.getHardwareSpec(), 1925 moduleController.getBottomBarSpec()); 1926 } 1927 } 1928 1929 /** 1930 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec} 1931 * to the bottom bar mode options based on limitations from a 1932 * {@link com.android.camera.hardware.HardwareSpec}. 1933 * 1934 * Options not supported by the hardware are either hidden 1935 * or disabled, depending on the option. 1936 * 1937 * Otherwise, the option is fully enabled and clickable. 1938 */ 1939 public void applyModuleSpecs(final HardwareSpec hardwareSpec, 1940 final BottomBarUISpec bottomBarSpec) { 1941 if (hardwareSpec == null || bottomBarSpec == null) { 1942 return; 1943 } 1944 1945 ButtonManager buttonManager = mController.getButtonManager(); 1946 SettingsManager settingsManager = mController.getSettingsManager(); 1947 1948 buttonManager.setToInitialState(); 1949 1950 /** Standard mode options */ 1951 if (mController.getCameraProvider().getNumberOfCameras() > 1 && 1952 hardwareSpec.isFrontCameraSupported()) { 1953 if (bottomBarSpec.enableCamera) { 1954 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA, 1955 bottomBarSpec.cameraCallback); 1956 } else { 1957 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA); 1958 } 1959 } else { 1960 // Hide camera icon if front camera not available. 1961 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA); 1962 } 1963 1964 if (bottomBarSpec.hideFlash) { 1965 // Hide both flash and torch button in flash disable logic 1966 buttonManager.hideButton(ButtonManager.BUTTON_FLASH); 1967 buttonManager.hideButton(ButtonManager.BUTTON_TORCH); 1968 } else { 1969 if (hardwareSpec.isFlashSupported()) { 1970 if (bottomBarSpec.enableFlash) { 1971 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH, 1972 bottomBarSpec.flashCallback); 1973 } else if (bottomBarSpec.enableTorchFlash) { 1974 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH, 1975 bottomBarSpec.flashCallback); 1976 } else if (bottomBarSpec.enableHdrPlusFlash) { 1977 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH, 1978 bottomBarSpec.flashCallback); 1979 } else { 1980 // Hide both flash and torch button in flash disable logic 1981 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1982 buttonManager.disableButton(ButtonManager.BUTTON_TORCH); 1983 } 1984 } else { 1985 // Disable both flash and torch icon if not supported 1986 // by the chosen camera hardware. 1987 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1988 buttonManager.disableButton(ButtonManager.BUTTON_TORCH); 1989 } 1990 } 1991 1992 if (bottomBarSpec.hideHdr || mIsCaptureIntent) { 1993 // Force hide hdr or hdr plus icon. 1994 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS); 1995 } else { 1996 if (hardwareSpec.isHdrPlusSupported()) { 1997 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager, 1998 mController.getModuleScope())) { 1999 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS, 2000 bottomBarSpec.hdrCallback); 2001 } else { 2002 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS); 2003 } 2004 } else if (hardwareSpec.isHdrSupported()) { 2005 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager, 2006 mController.getModuleScope())) { 2007 buttonManager.initializeButton(ButtonManager.BUTTON_HDR, 2008 bottomBarSpec.hdrCallback); 2009 } else { 2010 buttonManager.disableButton(ButtonManager.BUTTON_HDR); 2011 } 2012 } else { 2013 // Hide hdr plus or hdr icon if neither are supported. 2014 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS); 2015 } 2016 } 2017 2018 if (bottomBarSpec.hideGridLines) { 2019 // Force hide grid lines icon. 2020 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES); 2021 hideGridLines(); 2022 } else { 2023 if (bottomBarSpec.enableGridLines) { 2024 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES, 2025 bottomBarSpec.gridLinesCallback != null ? 2026 bottomBarSpec.gridLinesCallback : getGridLinesCallback() 2027 ); 2028 } else { 2029 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES); 2030 hideGridLines(); 2031 } 2032 } 2033 2034 if (bottomBarSpec.enableSelfTimer) { 2035 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null); 2036 } else { 2037 if (bottomBarSpec.showSelfTimer) { 2038 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN); 2039 } else { 2040 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN); 2041 } 2042 } 2043 2044 if (bottomBarSpec.enablePanoOrientation 2045 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) { 2046 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback); 2047 } 2048 2049 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation && 2050 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) && 2051 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL, 2052 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED); 2053 if (enableExposureCompensation) { 2054 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, 2055 new View.OnClickListener() { 2056 @Override 2057 public void onClick(View v) { 2058 mModeOptionsOverlay.showExposureOptions(); 2059 } 2060 }); 2061 buttonManager.setExposureCompensationParameters( 2062 bottomBarSpec.minExposureCompensation, 2063 bottomBarSpec.maxExposureCompensation, 2064 bottomBarSpec.exposureCompensationStep); 2065 2066 buttonManager.setExposureCompensationCallback( 2067 bottomBarSpec.exposureCompensationSetCallback); 2068 buttonManager.updateExposureButtons(); 2069 } else { 2070 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION); 2071 buttonManager.setExposureCompensationCallback(null); 2072 } 2073 2074 /** Intent UI */ 2075 if (bottomBarSpec.showCancel) { 2076 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL, 2077 bottomBarSpec.cancelCallback); 2078 } 2079 if (bottomBarSpec.showDone) { 2080 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE, 2081 bottomBarSpec.doneCallback); 2082 } 2083 if (bottomBarSpec.showRetake) { 2084 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE, 2085 bottomBarSpec.retakeCallback, 2086 R.drawable.ic_back, 2087 R.string.retake_button_description); 2088 } 2089 if (bottomBarSpec.showReview) { 2090 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW, 2091 bottomBarSpec.reviewCallback, 2092 R.drawable.ic_play, 2093 R.string.review_button_description); 2094 } 2095 } 2096 2097 /** 2098 * Shows the given tutorial on the screen. 2099 */ 2100 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) { 2101 tutorial.show(mTutorialsPlaceHolderWrapper, inflater); 2102 } 2103 2104 /***************************Filmstrip api *****************************/ 2105 2106 public void showFilmstrip() { 2107 mModeListView.onBackPressed(); 2108 mFilmstripLayout.showFilmstrip(); 2109 } 2110 2111 public void hideFilmstrip() { 2112 mFilmstripLayout.hideFilmstrip(); 2113 } 2114 2115 public int getFilmstripVisibility() { 2116 return mFilmstripLayout.getVisibility(); 2117 } 2118} 2119