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