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