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