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