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.LayoutInflater; 31 import android.view.MotionEvent; 32 import android.view.TextureView; 33 import android.view.View; 34 import android.view.ViewConfiguration; 35 import android.view.ViewGroup; 36 import android.view.accessibility.AccessibilityManager; 37 import android.widget.FrameLayout; 38 39 import com.android.camera.AnimationManager; 40 import com.android.camera.ButtonManager; 41 import com.android.camera.CaptureLayoutHelper; 42 import com.android.camera.ShutterButton; 43 import com.android.camera.TextureViewHelper; 44 import com.android.camera.debug.Log; 45 import com.android.camera.filmstrip.FilmstripContentPanel; 46 import com.android.camera.hardware.HardwareSpec; 47 import com.android.camera.module.ModuleController; 48 import com.android.camera.settings.Keys; 49 import com.android.camera.settings.SettingsManager; 50 import com.android.camera.ui.AbstractTutorialOverlay; 51 import com.android.camera.ui.BottomBar; 52 import com.android.camera.ui.BottomBarModeOptionsWrapper; 53 import com.android.camera.ui.CaptureAnimationOverlay; 54 import com.android.camera.ui.GridLines; 55 import com.android.camera.ui.MainActivityLayout; 56 import com.android.camera.ui.ModeListView; 57 import com.android.camera.ui.ModeTransitionView; 58 import com.android.camera.ui.PreviewOverlay; 59 import com.android.camera.ui.PreviewStatusListener; 60 import com.android.camera.ui.TouchCoordinate; 61 import com.android.camera.util.ApiHelper; 62 import com.android.camera.util.CameraUtil; 63 import com.android.camera.util.Gusterpolator; 64 import com.android.camera.util.PhotoSphereHelper; 65 import com.android.camera.widget.Cling; 66 import com.android.camera.widget.FilmstripLayout; 67 import com.android.camera.widget.IndicatorIconController; 68 import com.android.camera.widget.ModeOptionsOverlay; 69 import com.android.camera.widget.PeekView; 70 import com.android.camera2.R; 71 72 import java.util.List; 73 74 /** 75 * CameraAppUI centralizes control of views shared across modules. Whereas module 76 * specific views will be handled in each Module UI. For example, we can now 77 * bring the flash animation and capture animation up from each module to app 78 * level, as these animations are largely the same for all modules. 79 * 80 * This class also serves to disambiguate touch events. It recognizes all the 81 * swipe gestures that happen on the preview by attaching a touch listener to 82 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge 83 * of how swipe from each direction should be handled, it can then redirect these 84 * events to appropriate recipient views. 85 */ 86 public class CameraAppUI implements ModeListView.ModeSwitchListener, 87 TextureView.SurfaceTextureListener, 88 ModeListView.ModeListOpenListener, 89 SettingsManager.OnSettingChangedListener, 90 ShutterButton.OnShutterButtonListener { 91 92 /** 93 * The bottom controls on the filmstrip. 94 */ 95 public static interface BottomPanel { 96 /** Values for the view state of the button. */ 97 public final int VIEWER_NONE = 0; 98 public final int VIEWER_PHOTO_SPHERE = 1; 99 public final int VIEWER_REFOCUS = 2; 100 public final int VIEWER_OTHER = 3; 101 102 /** 103 * Sets a new or replaces an existing listener for bottom control events. 104 */ setListener(Listener listener)105 void setListener(Listener listener); 106 107 /** 108 * Sets cling for external viewer button. 109 */ setClingForViewer(int viewerType, Cling cling)110 void setClingForViewer(int viewerType, Cling cling); 111 112 /** 113 * Clears cling for external viewer button. 114 */ clearClingForViewer(int viewerType)115 void clearClingForViewer(int viewerType); 116 117 /** 118 * Returns a cling for the specified viewer type. 119 */ getClingForViewer(int viewerType)120 Cling getClingForViewer(int viewerType); 121 122 /** 123 * Set if the bottom controls are visible. 124 * @param visible {@code true} if visible. 125 */ setVisible(boolean visible)126 void setVisible(boolean visible); 127 128 /** 129 * @param visible Whether the button is visible. 130 */ setEditButtonVisibility(boolean visible)131 void setEditButtonVisibility(boolean visible); 132 133 /** 134 * @param enabled Whether the button is enabled. 135 */ setEditEnabled(boolean enabled)136 void setEditEnabled(boolean enabled); 137 138 /** 139 * Sets the visibility of the view-photosphere button. 140 * 141 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE}, 142 * {@link #VIEWER_REFOCUS}. 143 */ setViewerButtonVisibility(int state)144 void setViewerButtonVisibility(int state); 145 146 /** 147 * @param enabled Whether the button is enabled. 148 */ setViewEnabled(boolean enabled)149 void setViewEnabled(boolean enabled); 150 151 /** 152 * @param enabled Whether the button is enabled. 153 */ setTinyPlanetEnabled(boolean enabled)154 void setTinyPlanetEnabled(boolean enabled); 155 156 /** 157 * @param visible Whether the button is visible. 158 */ setDeleteButtonVisibility(boolean visible)159 void setDeleteButtonVisibility(boolean visible); 160 161 /** 162 * @param enabled Whether the button is enabled. 163 */ setDeleteEnabled(boolean enabled)164 void setDeleteEnabled(boolean enabled); 165 166 /** 167 * @param visible Whether the button is visible. 168 */ setShareButtonVisibility(boolean visible)169 void setShareButtonVisibility(boolean visible); 170 171 /** 172 * @param enabled Whether the button is enabled. 173 */ setShareEnabled(boolean enabled)174 void setShareEnabled(boolean enabled); 175 176 /** 177 * Sets the texts for progress UI. 178 * 179 * @param text The text to show. 180 */ setProgressText(CharSequence text)181 void setProgressText(CharSequence text); 182 183 /** 184 * Sets the progress. 185 * 186 * @param progress The progress value. Should be between 0 and 100. 187 */ setProgress(int progress)188 void setProgress(int progress); 189 190 /** 191 * Replaces the progress UI with an error message. 192 */ showProgressError(CharSequence message)193 void showProgressError(CharSequence message); 194 195 /** 196 * Hide the progress error message. 197 */ hideProgressError()198 void hideProgressError(); 199 200 /** 201 * Shows the progress. 202 */ showProgress()203 void showProgress(); 204 205 /** 206 * Hides the progress. 207 */ hideProgress()208 void hideProgress(); 209 210 /** 211 * Shows the controls. 212 */ showControls()213 void showControls(); 214 215 /** 216 * Hides the controls. 217 */ hideControls()218 void hideControls(); 219 220 /** 221 * Classes implementing this interface can listen for events on the bottom 222 * controls. 223 */ 224 public static interface Listener { 225 /** 226 * Called when the user pressed the "view" button to e.g. view a photo 227 * sphere or RGBZ image. 228 */ onExternalViewer()229 public void onExternalViewer(); 230 231 /** 232 * Called when the "edit" button is pressed. 233 */ onEdit()234 public void onEdit(); 235 236 /** 237 * Called when the "tiny planet" button is pressed. 238 */ onTinyPlanet()239 public void onTinyPlanet(); 240 241 /** 242 * Called when the "delete" button is pressed. 243 */ onDelete()244 public void onDelete(); 245 246 /** 247 * Called when the "share" button is pressed. 248 */ onShare()249 public void onShare(); 250 251 /** 252 * Called when the progress error message is clicked. 253 */ onProgressErrorClicked()254 public void onProgressErrorClicked(); 255 } 256 } 257 258 /** 259 * BottomBarUISpec provides a structure for modules 260 * to specify their ideal bottom bar mode options layout. 261 * 262 * Once constructed by a module, this class should be 263 * treated as read only. 264 * 265 * The application then edits this spec according to 266 * hardware limitations and displays the final bottom 267 * bar ui. 268 */ 269 public static class BottomBarUISpec { 270 /** Mode options UI */ 271 272 /** 273 * Set true if the camera option should be enabled. 274 * If not set or false, and multiple cameras are supported, 275 * the camera option will be disabled. 276 * 277 * If multiple cameras are not supported, this preference 278 * is ignored and the camera option will not be visible. 279 */ 280 public boolean enableCamera; 281 282 /** 283 * Set true if the camera option should not be visible, regardless 284 * of hardware limitations. 285 */ 286 public boolean hideCamera; 287 288 /** 289 * Set true if the photo flash option should be enabled. 290 * If not set or false, the photo flash option will be 291 * disabled. 292 * 293 * If the hardware does not support multiple flash values, 294 * this preference is ignored and the flash option will 295 * be disabled. It will not be made invisible in order to 296 * preserve a consistent experience across devices and between 297 * front and back cameras. 298 */ 299 public boolean enableFlash; 300 301 /** 302 * Set true if the video flash option should be enabled. 303 * Same disable rules apply as the photo flash option. 304 */ 305 public boolean enableTorchFlash; 306 307 /** 308 * Set true if the HDR+ flash option should be enabled. 309 * Same disable rules apply as the photo flash option. 310 */ 311 public boolean enableHdrPlusFlash; 312 313 /** 314 * Set true if flash should not be visible, regardless of 315 * hardware limitations. 316 */ 317 public boolean hideFlash; 318 319 /** 320 * Set true if the hdr/hdr+ option should be enabled. 321 * If not set or false, the hdr/hdr+ option will be disabled. 322 * 323 * Hdr or hdr+ will be chosen based on hardware limitations, 324 * with hdr+ prefered. 325 * 326 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+ 327 * will not be visible. 328 */ 329 public boolean enableHdr; 330 331 /** 332 * Set true if hdr/hdr+ should not be visible, regardless of 333 * hardware limitations. 334 */ 335 public boolean hideHdr; 336 337 /** 338 * Set true if grid lines should be visible. Not setting this 339 * causes grid lines to be disabled. This option is agnostic to 340 * the hardware. 341 */ 342 public boolean enableGridLines; 343 344 /** 345 * Set true if grid lines should not be visible. 346 */ 347 public boolean hideGridLines; 348 349 /** 350 * Set true if the panorama orientation option should be visible. 351 * 352 * This option is not constrained by hardware limitations. 353 */ 354 public boolean enablePanoOrientation; 355 356 public boolean enableExposureCompensation; 357 358 /** Intent UI */ 359 360 /** 361 * Set true if the intent ui cancel option should be visible. 362 */ 363 public boolean showCancel; 364 /** 365 * Set true if the intent ui done option should be visible. 366 */ 367 public boolean showDone; 368 /** 369 * Set true if the intent ui retake option should be visible. 370 */ 371 public boolean showRetake; 372 /** 373 * Set true if the intent ui review option should be visible. 374 */ 375 public boolean showReview; 376 377 /** Mode options callbacks */ 378 379 /** 380 * A {@link com.android.camera.ButtonManager.ButtonCallback} 381 * that will be executed when the camera option is pressed. This 382 * callback can be null. 383 */ 384 public ButtonManager.ButtonCallback cameraCallback; 385 386 /** 387 * A {@link com.android.camera.ButtonManager.ButtonCallback} 388 * that will be executed when the flash option is pressed. This 389 * callback can be null. 390 */ 391 public ButtonManager.ButtonCallback flashCallback; 392 393 /** 394 * A {@link com.android.camera.ButtonManager.ButtonCallback} 395 * that will be executed when the hdr/hdr+ option is pressed. This 396 * callback can be null. 397 */ 398 public ButtonManager.ButtonCallback hdrCallback; 399 400 /** 401 * A {@link com.android.camera.ButtonManager.ButtonCallback} 402 * that will be executed when the grid lines option is pressed. This 403 * callback can be null. 404 */ 405 public ButtonManager.ButtonCallback gridLinesCallback; 406 407 /** 408 * A {@link com.android.camera.ButtonManager.ButtonCallback} 409 * that will execute when the panorama orientation option is pressed. 410 * This callback can be null. 411 */ 412 public ButtonManager.ButtonCallback panoOrientationCallback; 413 414 /** Intent UI callbacks */ 415 416 /** 417 * A {@link android.view.View.OnClickListener} that will execute 418 * when the cancel option is pressed. This callback can be null. 419 */ 420 public View.OnClickListener cancelCallback; 421 422 /** 423 * A {@link android.view.View.OnClickListener} that will execute 424 * when the done option is pressed. This callback can be null. 425 */ 426 public View.OnClickListener doneCallback; 427 428 /** 429 * A {@link android.view.View.OnClickListener} that will execute 430 * when the retake option is pressed. This callback can be null. 431 */ 432 public View.OnClickListener retakeCallback; 433 434 /** 435 * A {@link android.view.View.OnClickListener} that will execute 436 * when the review option is pressed. This callback can be null. 437 */ 438 public View.OnClickListener reviewCallback; 439 440 /** 441 * A ExposureCompensationSetCallback that will execute 442 * when an expsosure button is pressed. This callback can be null. 443 */ 444 public interface ExposureCompensationSetCallback { setExposure(int value)445 public void setExposure(int value); 446 } 447 public ExposureCompensationSetCallback exposureCompensationSetCallback; 448 449 /** 450 * Exposure compensation parameters. 451 */ 452 public int minExposureCompensation; 453 public int maxExposureCompensation; 454 public float exposureCompensationStep; 455 456 /** 457 * Whether self-timer is enabled. 458 */ 459 public boolean enableSelfTimer = false; 460 461 /** 462 * Whether the option for self-timer should show. If true and 463 * {@link #enableSelfTimer} is false, then the option should be shown 464 * disabled. 465 */ 466 public boolean showSelfTimer = false; 467 } 468 469 470 private final static Log.Tag TAG = new Log.Tag("CameraAppUI"); 471 472 private final AppController mController; 473 private final boolean mIsCaptureIntent; 474 private final AnimationManager mAnimationManager; 475 476 // Swipe states: 477 private final static int IDLE = 0; 478 private final static int SWIPE_UP = 1; 479 private final static int SWIPE_DOWN = 2; 480 private final static int SWIPE_LEFT = 3; 481 private final static int SWIPE_RIGHT = 4; 482 private boolean mSwipeEnabled = true; 483 484 // Shared Surface Texture properities. 485 private SurfaceTexture mSurface; 486 private int mSurfaceWidth; 487 private int mSurfaceHeight; 488 489 // Touch related measures: 490 private final int mSlop; 491 private final static int SWIPE_TIME_OUT_MS = 500; 492 493 // Mode cover states: 494 private final static int COVER_HIDDEN = 0; 495 private final static int COVER_SHOWN = 1; 496 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2; 497 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3; 498 499 /** 500 * Preview down-sample rate when taking a screenshot. 501 */ 502 private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2; 503 504 // App level views: 505 private final FrameLayout mCameraRootView; 506 private final ModeTransitionView mModeTransitionView; 507 private final MainActivityLayout mAppRootView; 508 private final ModeListView mModeListView; 509 private final FilmstripLayout mFilmstripLayout; 510 private TextureView mTextureView; 511 private FrameLayout mModuleUI; 512 private ShutterButton mShutterButton; 513 private BottomBar mBottomBar; 514 private ModeOptionsOverlay mModeOptionsOverlay; 515 private IndicatorIconController mIndicatorIconController; 516 private View mFocusOverlay; 517 private FrameLayout mTutorialsPlaceHolderWrapper; 518 private BottomBarModeOptionsWrapper mIndicatorBottomBarWrapper; 519 private TextureViewHelper mTextureViewHelper; 520 private final GestureDetector mGestureDetector; 521 private DisplayManager.DisplayListener mDisplayListener; 522 private int mLastRotation; 523 private int mSwipeState = IDLE; 524 private PreviewOverlay mPreviewOverlay; 525 private GridLines mGridLines; 526 private CaptureAnimationOverlay mCaptureOverlay; 527 private PreviewStatusListener mPreviewStatusListener; 528 private int mModeCoverState = COVER_HIDDEN; 529 private final FilmstripBottomPanel mFilmstripBottomControls; 530 private final FilmstripContentPanel mFilmstripPanel; 531 private Runnable mHideCoverRunnable; 532 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener 533 = new View.OnLayoutChangeListener() { 534 @Override 535 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 536 int oldTop, int oldRight, int oldBottom) { 537 if (mPreviewStatusListener != null) { 538 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft, 539 oldTop, oldRight, oldBottom); 540 } 541 } 542 }; 543 private View mModeOptionsToggle; 544 private final PeekView mPeekView; 545 private final CaptureLayoutHelper mCaptureLayoutHelper; 546 private boolean mAccessibilityEnabled; 547 private final View mAccessibilityAffordances; 548 549 private boolean mDisableAllUserInteractions; 550 /** 551 * Provides current preview frame and the controls/overlay from the module that 552 * are shown on top of the preview. 553 */ 554 public interface CameraModuleScreenShotProvider { 555 /** 556 * Returns the current preview frame down-sampled using the given down-sample 557 * factor. 558 * 559 * @param downSampleFactor the down sample factor for down sampling the 560 * preview frame. (e.g. a down sample factor of 561 * 2 means to scale down the preview frame to 1/2 562 * the width and height.) 563 * @return down-sampled preview frame 564 */ getPreviewFrame(int downSampleFactor)565 public Bitmap getPreviewFrame(int downSampleFactor); 566 567 /** 568 * @return the controls and overlays that are currently showing on top of 569 * the preview drawn into a bitmap with no scaling applied. 570 */ getPreviewOverlayAndControls()571 public Bitmap getPreviewOverlayAndControls(); 572 573 /** 574 * Returns a bitmap containing the current screenshot. 575 * 576 * @param previewDownSampleFactor the downsample factor applied on the 577 * preview frame when taking the screenshot 578 */ getScreenShot(int previewDownSampleFactor)579 public Bitmap getScreenShot(int previewDownSampleFactor); 580 } 581 582 /** 583 * This listener gets called when the size of the window (excluding the system 584 * decor such as status bar and nav bar) has changed. 585 */ 586 public interface NonDecorWindowSizeChangedListener { onNonDecorWindowSizeChanged(int width, int height, int rotation)587 public void onNonDecorWindowSizeChanged(int width, int height, int rotation); 588 } 589 590 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider = 591 new CameraModuleScreenShotProvider() { 592 @Override 593 public Bitmap getPreviewFrame(int downSampleFactor) { 594 if (mCameraRootView == null || mTextureView == null) { 595 return null; 596 } 597 // Gets the bitmap from the preview TextureView. 598 Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor); 599 return preview; 600 } 601 602 @Override 603 public Bitmap getPreviewOverlayAndControls() { 604 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(), 605 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888); 606 Canvas canvas = new Canvas(overlays); 607 mCameraRootView.draw(canvas); 608 return overlays; 609 } 610 611 @Override 612 public Bitmap getScreenShot(int previewDownSampleFactor) { 613 Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(), 614 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888); 615 Canvas canvas = new Canvas(screenshot); 616 canvas.drawARGB(255, 0, 0, 0); 617 Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor); 618 if (preview != null) { 619 canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null); 620 } 621 Bitmap overlay = getPreviewOverlayAndControls(); 622 if (overlay != null) { 623 canvas.drawBitmap(overlay, 0f, 0f, null); 624 } 625 return screenshot; 626 } 627 }; 628 629 private long mCoverHiddenTime = -1; // System time when preview cover was hidden. 630 getCoverHiddenTime()631 public long getCoverHiddenTime() { 632 return mCoverHiddenTime; 633 } 634 635 /** 636 * This resets the preview to have no applied transform matrix. 637 */ clearPreviewTransform()638 public void clearPreviewTransform() { 639 mTextureViewHelper.clearTransform(); 640 } 641 updatePreviewAspectRatio(float aspectRatio)642 public void updatePreviewAspectRatio(float aspectRatio) { 643 mTextureViewHelper.updateAspectRatio(aspectRatio); 644 } 645 646 /** 647 * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of 648 * its TextureView. This is necessary to get the expected behavior for the TextureView's 649 * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the 650 * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer 651 * size). 652 * 653 * b/17286155 - Tracking a fix for this in HardwareLayer. 654 */ setDefaultBufferSizeToViewDimens()655 public void setDefaultBufferSizeToViewDimens() { 656 if (mSurface == null || mTextureView == null) { 657 Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup"); 658 return; 659 } 660 mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight()); 661 } 662 663 /** 664 * Updates the preview matrix without altering it. 665 * 666 * @param matrix 667 * @param aspectRatio the desired aspect ratio for the preview. 668 */ updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio)669 public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) { 670 mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio); 671 } 672 673 /** 674 * @return the rect that will display the preview. 675 */ getFullscreenRect()676 public RectF getFullscreenRect() { 677 return mTextureViewHelper.getFullscreenRect(); 678 } 679 680 /** 681 * This is to support modules that calculate their own transform matrix because 682 * they need to use a transform matrix to rotate the preview. 683 * 684 * @param matrix transform matrix to be set on the TextureView 685 */ updatePreviewTransform(Matrix matrix)686 public void updatePreviewTransform(Matrix matrix) { 687 mTextureViewHelper.updateTransform(matrix); 688 } 689 690 public interface AnimationFinishedListener { onAnimationFinished(boolean success)691 public void onAnimationFinished(boolean success); 692 } 693 694 private class MyTouchListener implements View.OnTouchListener { 695 private boolean mScaleStarted = false; 696 @Override onTouch(View v, MotionEvent event)697 public boolean onTouch(View v, MotionEvent event) { 698 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 699 mScaleStarted = false; 700 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { 701 mScaleStarted = true; 702 } 703 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event); 704 } 705 } 706 707 /** 708 * This gesture listener finds out the direction of the scroll gestures and 709 * sends them to CameraAppUI to do further handling. 710 */ 711 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener { 712 private MotionEvent mDown; 713 714 @Override onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY)715 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) { 716 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS 717 || mSwipeState != IDLE 718 || mIsCaptureIntent 719 || !mSwipeEnabled) { 720 return false; 721 } 722 723 int deltaX = (int) (ev.getX() - mDown.getX()); 724 int deltaY = (int) (ev.getY() - mDown.getY()); 725 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { 726 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) { 727 // Calculate the direction of the swipe. 728 if (deltaX >= Math.abs(deltaY)) { 729 // Swipe right. 730 setSwipeState(SWIPE_RIGHT); 731 } else if (deltaX <= -Math.abs(deltaY)) { 732 // Swipe left. 733 setSwipeState(SWIPE_LEFT); 734 } 735 } 736 } 737 return true; 738 } 739 setSwipeState(int swipeState)740 private void setSwipeState(int swipeState) { 741 mSwipeState = swipeState; 742 // Notify new swipe detected. 743 onSwipeDetected(swipeState); 744 } 745 746 @Override onDown(MotionEvent ev)747 public boolean onDown(MotionEvent ev) { 748 mDown = MotionEvent.obtain(ev); 749 mSwipeState = IDLE; 750 return false; 751 } 752 } 753 CameraAppUI(AppController controller, MainActivityLayout appRootView, boolean isCaptureIntent)754 public CameraAppUI(AppController controller, MainActivityLayout appRootView, 755 boolean isCaptureIntent) { 756 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop(); 757 mController = controller; 758 mIsCaptureIntent = isCaptureIntent; 759 760 mAppRootView = appRootView; 761 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout); 762 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root); 763 mModeTransitionView = (ModeTransitionView) 764 mAppRootView.findViewById(R.id.mode_transition_view); 765 mFilmstripBottomControls = new FilmstripBottomPanel(controller, 766 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel)); 767 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout); 768 mGestureDetector = new GestureDetector(controller.getAndroidContext(), 769 new MyGestureListener()); 770 Resources res = controller.getAndroidContext().getResources(); 771 mCaptureLayoutHelper = new CaptureLayoutHelper( 772 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min), 773 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max), 774 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal)); 775 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout); 776 if (mModeListView != null) { 777 mModeListView.setModeSwitchListener(this); 778 mModeListView.setModeListOpenListener(this); 779 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider); 780 mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper); 781 boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean( 782 SettingsManager.SCOPE_GLOBAL, 783 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING); 784 mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling); 785 } else { 786 Log.e(TAG, "Cannot find mode list in the view hierarchy"); 787 } 788 mAnimationManager = new AnimationManager(); 789 mPeekView = (PeekView) appRootView.findViewById(R.id.peek_view); 790 mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper); 791 initDisplayListener(); 792 mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances); 793 View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button); 794 modeListToggle.setOnClickListener(new View.OnClickListener() { 795 @Override 796 public void onClick(View view) { 797 openModeList(); 798 } 799 }); 800 View filmstripToggle = mAppRootView.findViewById( 801 R.id.accessibility_filmstrip_toggle_button); 802 filmstripToggle.setOnClickListener(new View.OnClickListener() { 803 @Override 804 public void onClick(View view) { 805 showFilmstrip(); 806 } 807 }); 808 } 809 810 811 /** 812 * Freeze what is currently shown on screen until the next preview frame comes 813 * in. 814 */ freezeScreenUntilPreviewReady()815 public void freezeScreenUntilPreviewReady() { 816 Log.v(TAG, "freezeScreenUntilPreviewReady"); 817 mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider 818 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT)); 819 mHideCoverRunnable = new Runnable() { 820 @Override 821 public void run() { 822 mModeTransitionView.hideImageCover(); 823 } 824 }; 825 mModeCoverState = COVER_SHOWN; 826 } 827 828 /** 829 * Creates a cling for the specific viewer and links the cling to the corresponding 830 * button for layout position. 831 * 832 * @param viewerType defines which viewer the cling is for. 833 */ setupClingForViewer(int viewerType)834 public void setupClingForViewer(int viewerType) { 835 if (viewerType == BottomPanel.VIEWER_REFOCUS) { 836 FrameLayout filmstripContent = (FrameLayout) mAppRootView 837 .findViewById(R.id.camera_filmstrip_content_layout); 838 if (filmstripContent != null) { 839 // Creates refocus cling. 840 LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext() 841 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 842 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false); 843 // Sets instruction text in the cling. 844 refocusCling.setText(mController.getAndroidContext().getResources() 845 .getString(R.string.cling_text_for_refocus_editor_button)); 846 847 // Adds cling into view hierarchy. 848 int clingWidth = mController.getAndroidContext() 849 .getResources().getDimensionPixelSize(R.dimen.default_cling_width); 850 filmstripContent.addView(refocusCling, clingWidth, 851 ViewGroup.LayoutParams.WRAP_CONTENT); 852 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling); 853 } 854 } 855 } 856 857 /** 858 * Clears the listeners for the cling and remove it from the view hierarchy. 859 * 860 * @param viewerType defines which viewer the cling is for. 861 */ clearClingForViewer(int viewerType)862 public void clearClingForViewer(int viewerType) { 863 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType); 864 if (clingToBeRemoved == null) { 865 // No cling is created for the specific viewer type. 866 return; 867 } 868 mFilmstripBottomControls.clearClingForViewer(viewerType); 869 clingToBeRemoved.setVisibility(View.GONE); 870 mAppRootView.removeView(clingToBeRemoved); 871 } 872 873 /** 874 * Enable or disable swipe gestures. We want to disable them e.g. while we 875 * record a video. 876 */ setSwipeEnabled(boolean enabled)877 public void setSwipeEnabled(boolean enabled) { 878 mSwipeEnabled = enabled; 879 // TODO: This can be removed once we come up with a new design for handling swipe 880 // on shutter button and mode options. (More details: b/13751653) 881 mAppRootView.setSwipeEnabled(enabled); 882 } 883 onDestroy()884 public void onDestroy() { 885 ((DisplayManager) mController.getAndroidContext() 886 .getSystemService(Context.DISPLAY_SERVICE)) 887 .unregisterDisplayListener(mDisplayListener); 888 } 889 890 /** 891 * Initializes the display listener to listen to display changes such as 892 * 180-degree rotation change, which will not have an onConfigurationChanged 893 * callback. 894 */ initDisplayListener()895 private void initDisplayListener() { 896 if (ApiHelper.HAS_DISPLAY_LISTENER) { 897 mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext()); 898 899 mDisplayListener = new DisplayManager.DisplayListener() { 900 @Override 901 public void onDisplayAdded(int arg0) { 902 // Do nothing. 903 } 904 905 @Override 906 public void onDisplayChanged(int displayId) { 907 int rotation = CameraUtil.getDisplayRotation( 908 mController.getAndroidContext()); 909 if ((rotation - mLastRotation + 360) % 360 == 180 910 && mPreviewStatusListener != null) { 911 mPreviewStatusListener.onPreviewFlipped(); 912 mIndicatorBottomBarWrapper.requestLayout(); 913 mModeListView.requestLayout(); 914 mTextureView.requestLayout(); 915 } 916 mLastRotation = rotation; 917 } 918 919 @Override 920 public void onDisplayRemoved(int arg0) { 921 // Do nothing. 922 } 923 }; 924 925 ((DisplayManager) mController.getAndroidContext() 926 .getSystemService(Context.DISPLAY_SERVICE)) 927 .registerDisplayListener(mDisplayListener, null); 928 } 929 } 930 931 /** 932 * Redirects touch events to appropriate recipient views based on swipe direction. 933 * More specifically, swipe up and swipe down will be handled by the view that handles 934 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected 935 * to mode list in order to bring up mode list. 936 */ onSwipeDetected(int swipeState)937 private void onSwipeDetected(int swipeState) { 938 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) { 939 // TODO: Polish quick switch after this release. 940 // Quick switch between modes. 941 int currentModuleIndex = mController.getCurrentModuleIndex(); 942 final int moduleToTransitionTo = 943 mController.getQuickSwitchToModuleId(currentModuleIndex); 944 if (currentModuleIndex != moduleToTransitionTo) { 945 mAppRootView.redirectTouchEventsTo(mModeTransitionView); 946 int shadeColorId = R.color.mode_cover_default_color; 947 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo, 948 mController.getAndroidContext()); 949 950 AnimationFinishedListener listener = new AnimationFinishedListener() { 951 @Override 952 public void onAnimationFinished(boolean success) { 953 if (success) { 954 mHideCoverRunnable = new Runnable() { 955 @Override 956 public void run() { 957 mModeTransitionView.startPeepHoleAnimation(); 958 } 959 }; 960 mModeCoverState = COVER_SHOWN; 961 // Go to new module when the previous operation is successful. 962 mController.onModeSelected(moduleToTransitionTo); 963 } 964 } 965 }; 966 } 967 } else if (swipeState == SWIPE_LEFT) { 968 // Pass the touch sequence to filmstrip layout. 969 mAppRootView.redirectTouchEventsTo(mFilmstripLayout); 970 } else if (swipeState == SWIPE_RIGHT) { 971 // Pass the touch to mode switcher 972 mAppRootView.redirectTouchEventsTo(mModeListView); 973 } 974 } 975 976 /** 977 * Gets called when activity resumes in preview. 978 */ resume()979 public void resume() { 980 // Show mode theme cover until preview is ready 981 showModeCoverUntilPreviewReady(); 982 983 // Hide action bar first since we are in full screen mode first, and 984 // switch the system UI to lights-out mode. 985 mFilmstripPanel.hide(); 986 987 // Show UI that is meant to only be used when spoken feedback is 988 // enabled. 989 mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled(); 990 mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE); 991 } 992 993 /** 994 * @return Whether any spoken feedback accessibility feature is currently 995 * enabled. 996 */ isSpokenFeedbackAccessibilityEnabled()997 private boolean isSpokenFeedbackAccessibilityEnabled() { 998 AccessibilityManager accessibilityManager = (AccessibilityManager) mController 999 .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE); 1000 List<AccessibilityServiceInfo> infos = accessibilityManager 1001 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN); 1002 return infos != null && !infos.isEmpty(); 1003 } 1004 1005 /** 1006 * Opens the mode list (e.g. because of the menu button being pressed) and 1007 * adapts the rest of the UI. 1008 */ openModeList()1009 public void openModeList() { 1010 mModeOptionsOverlay.closeModeOptions(); 1011 mModeListView.onMenuPressed(); 1012 } 1013 1014 /** 1015 * A cover view showing the mode theme color and mode icon will be visible on 1016 * top of preview until preview is ready (i.e. camera preview is started and 1017 * the first frame has been received). 1018 */ showModeCoverUntilPreviewReady()1019 private void showModeCoverUntilPreviewReady() { 1020 int modeId = mController.getCurrentModuleIndex(); 1021 int colorId = R.color.mode_cover_default_color;; 1022 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext()); 1023 mModeTransitionView.setupModeCover(colorId, iconId); 1024 mHideCoverRunnable = new Runnable() { 1025 @Override 1026 public void run() { 1027 mModeTransitionView.hideModeCover(null); 1028 if (!mDisableAllUserInteractions) { 1029 showShimmyDelayed(); 1030 } 1031 } 1032 }; 1033 mModeCoverState = COVER_SHOWN; 1034 } 1035 showShimmyDelayed()1036 private void showShimmyDelayed() { 1037 if (!mIsCaptureIntent) { 1038 // Show shimmy in SHIMMY_DELAY_MS 1039 mModeListView.showModeSwitcherHint(); 1040 } 1041 } 1042 hideModeCover()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 onPreviewVisiblityChanged(int visibility)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 */ pausePreviewRendering()1072 public void pausePreviewRendering() { 1073 mTextureView.setVisibility(View.INVISIBLE); 1074 } 1075 1076 /** 1077 * Call to begin rendering the preview again. 1078 */ resumePreviewRendering()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 */ getPreviewTransform(Matrix m)1090 public Matrix getPreviewTransform(Matrix m) { 1091 return mTextureView.getTransform(m); 1092 } 1093 1094 @Override onOpenFullScreen()1095 public void onOpenFullScreen() { 1096 // Do nothing. 1097 } 1098 1099 @Override onModeListOpenProgress(float progress)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 onModeListClosed()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 */ onBackPressed()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 */ setPreviewStatusListener(PreviewStatusListener previewStatusListener)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 */ onPreviewListenerChanged()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 */ onChangeCamera()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 */ addPreviewAreaChangedListener( PreviewStatusListener.PreviewAreaChangedListener listener)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 */ removePreviewAreaChangedListener( PreviewStatusListener.PreviewAreaChangedListener listener)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 */ prepareModuleUI()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 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines); 1238 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines); 1239 1240 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay); 1241 mPreviewOverlay.setOnTouchListener(new MyTouchListener()); 1242 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay); 1243 1244 mCaptureOverlay = (CaptureAnimationOverlay) 1245 mCameraRootView.findViewById(R.id.capture_overlay); 1246 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay); 1247 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay); 1248 1249 if (mIndicatorIconController == null) { 1250 mIndicatorIconController = 1251 new IndicatorIconController(mController, mAppRootView); 1252 } 1253 1254 mController.getButtonManager().load(mCameraRootView); 1255 mController.getButtonManager().setListener(mIndicatorIconController); 1256 mController.getSettingsManager().addListener(mIndicatorIconController); 1257 1258 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle); 1259 mFocusOverlay = mCameraRootView.findViewById(R.id.focus_overlay); 1260 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView 1261 .findViewById(R.id.tutorials_placeholder_wrapper); 1262 mIndicatorBottomBarWrapper = (BottomBarModeOptionsWrapper) mAppRootView 1263 .findViewById(R.id.indicator_bottombar_wrapper); 1264 mIndicatorBottomBarWrapper.setCaptureLayoutHelper(mCaptureLayoutHelper); 1265 mTextureViewHelper.addPreviewAreaSizeChangedListener( 1266 new PreviewStatusListener.PreviewAreaChangedListener() { 1267 @Override 1268 public void onPreviewAreaChanged(RectF previewArea) { 1269 mPeekView.setTranslationX(previewArea.right - mAppRootView.getRight()); 1270 } 1271 }); 1272 1273 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView); 1274 mTextureViewHelper.addAspectRatioChangedListener( 1275 new PreviewStatusListener.PreviewAspectRatioChangedListener() { 1276 @Override 1277 public void onPreviewAspectRatioChanged(float aspectRatio) { 1278 mModeOptionsOverlay.requestLayout(); 1279 mBottomBar.requestLayout(); 1280 } 1281 } 1282 ); 1283 } 1284 1285 /** 1286 * Called indirectly from each module in their initialization to get a view group 1287 * to inflate the module specific views in. 1288 * 1289 * @return a view group for modules to attach views to 1290 */ getModuleRootView()1291 public FrameLayout getModuleRootView() { 1292 // TODO: Change it to mModuleUI when refactor is done 1293 return mCameraRootView; 1294 } 1295 1296 /** 1297 * Remove all the module specific views. 1298 */ clearModuleUI()1299 public void clearModuleUI() { 1300 if (mModuleUI != null) { 1301 mModuleUI.removeAllViews(); 1302 } 1303 removeShutterListener(mController.getCurrentModuleController()); 1304 mTutorialsPlaceHolderWrapper.removeAllViews(); 1305 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE); 1306 1307 setShutterButtonEnabled(true); 1308 mPreviewStatusListener = null; 1309 mPreviewOverlay.reset(); 1310 mFocusOverlay.setVisibility(View.INVISIBLE); 1311 } 1312 1313 /** 1314 * Gets called when preview is ready to start. It sets up one shot preview callback 1315 * in order to receive a callback when the preview frame is available, so that 1316 * the preview cover can be hidden to reveal preview. 1317 * 1318 * An alternative for getting the timing to hide preview cover is through 1319 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)}, 1320 * which is less accurate but therefore is the fallback for modules that manage 1321 * their own preview callbacks (as setting one preview callback will override 1322 * any other installed preview callbacks), or use camera2 API. 1323 */ onPreviewReadyToStart()1324 public void onPreviewReadyToStart() { 1325 if (mModeCoverState == COVER_SHOWN) { 1326 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME; 1327 mController.setupOneShotPreviewListener(); 1328 } 1329 } 1330 1331 /** 1332 * Gets called when preview is started. 1333 */ onPreviewStarted()1334 public void onPreviewStarted() { 1335 Log.v(TAG, "onPreviewStarted"); 1336 if (mModeCoverState == COVER_SHOWN) { 1337 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE; 1338 } 1339 enableModeOptions(); 1340 } 1341 1342 /** 1343 * Gets notified when next preview frame comes in. 1344 */ onNewPreviewFrame()1345 public void onNewPreviewFrame() { 1346 Log.v(TAG, "onNewPreviewFrame"); 1347 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1348 hideModeCover(); 1349 } 1350 1351 @Override onShutterButtonClick()1352 public void onShutterButtonClick() { 1353 /* 1354 * Set the mode options toggle unclickable, generally 1355 * throughout the app, whenever the shutter button is clicked. 1356 * 1357 * This could be done in the OnShutterButtonListener of the 1358 * ModeOptionsOverlay, but since it is very important that we 1359 * can clearly see when the toggle becomes clickable again, 1360 * keep all of that logic at this level. 1361 */ 1362 disableModeOptions(); 1363 } 1364 1365 @Override onShutterCoordinate(TouchCoordinate coord)1366 public void onShutterCoordinate(TouchCoordinate coord) { 1367 // Do nothing. 1368 } 1369 1370 @Override onShutterButtonFocus(boolean pressed)1371 public void onShutterButtonFocus(boolean pressed) { 1372 // noop 1373 } 1374 1375 /** 1376 * Set the mode options toggle clickable. 1377 */ enableModeOptions()1378 public void enableModeOptions() { 1379 /* 1380 * For modules using camera 1 api, this gets called in 1381 * onSurfaceTextureUpdated whenever the preview gets stopped and 1382 * started after each capture. This also takes care of the 1383 * case where the mode options might be unclickable when we 1384 * switch modes 1385 * 1386 * For modules using camera 2 api, they're required to call this 1387 * method when a capture is "completed". Unfortunately this differs 1388 * per module implementation. 1389 */ 1390 if (!mDisableAllUserInteractions) { 1391 mModeOptionsOverlay.setToggleClickable(true); 1392 } 1393 } 1394 1395 /** 1396 * Set the mode options toggle not clickable. 1397 */ disableModeOptions()1398 public void disableModeOptions() { 1399 mModeOptionsOverlay.setToggleClickable(false); 1400 } 1401 setDisableAllUserInteractions(boolean disable)1402 public void setDisableAllUserInteractions(boolean disable) { 1403 if (disable) { 1404 disableModeOptions(); 1405 setShutterButtonEnabled(false); 1406 setSwipeEnabled(false); 1407 mModeListView.hideAnimated(); 1408 } else { 1409 enableModeOptions(); 1410 setShutterButtonEnabled(true); 1411 setSwipeEnabled(true); 1412 } 1413 mDisableAllUserInteractions = disable; 1414 } 1415 1416 /** 1417 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView} 1418 * 1419 * @param modeIndex mode index of the selected mode 1420 */ 1421 @Override onModeSelected(int modeIndex)1422 public void onModeSelected(int modeIndex) { 1423 mHideCoverRunnable = new Runnable() { 1424 @Override 1425 public void run() { 1426 mModeListView.startModeSelectionAnimation(); 1427 } 1428 }; 1429 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED); 1430 mModeCoverState = COVER_SHOWN; 1431 1432 int lastIndex = mController.getCurrentModuleIndex(); 1433 // Actual mode teardown / new mode initialization happens here 1434 mController.onModeSelected(modeIndex); 1435 int currentIndex = mController.getCurrentModuleIndex(); 1436 1437 if (lastIndex == currentIndex) { 1438 hideModeCover(); 1439 } 1440 1441 updateModeSpecificUIColors(); 1442 } 1443 updateModeSpecificUIColors()1444 private void updateModeSpecificUIColors() { 1445 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex()); 1446 } 1447 1448 @Override onSettingsSelected()1449 public void onSettingsSelected() { 1450 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL, 1451 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false); 1452 mModeListView.setShouldShowSettingsCling(false); 1453 mController.onSettingsSelected(); 1454 } 1455 1456 @Override getCurrentModeIndex()1457 public int getCurrentModeIndex() { 1458 return mController.getCurrentModuleIndex(); 1459 } 1460 1461 /********************** Capture animation **********************/ 1462 /* TODO: This session is subject to UX changes. In addition to the generic 1463 flash animation and post capture animation, consider designating a parameter 1464 for specifying the type of animation, as well as an animation finished listener 1465 so that modules can have more knowledge of the status of the animation. */ 1466 1467 /** 1468 * Starts the filmstrip peek animation. 1469 * 1470 * @param bitmap The bitmap to show. 1471 * @param strong Whether the animation shows more portion of the bitmap or 1472 * not. 1473 * @param accessibilityString An accessibility String to be announced 1474 during the peek animation. 1475 */ startPeekAnimation(Bitmap bitmap, boolean strong, String accessibilityString)1476 public void startPeekAnimation(Bitmap bitmap, boolean strong, String accessibilityString) { 1477 if (mFilmstripLayout.getVisibility() == View.VISIBLE) { 1478 return; 1479 } 1480 mPeekView.startPeekAnimation(bitmap, strong, accessibilityString); 1481 } 1482 1483 /** 1484 * Starts the pre-capture animation. 1485 * 1486 * @param shortFlash show shortest possible flash instead of regular long version. 1487 */ startPreCaptureAnimation(boolean shortFlash)1488 public void startPreCaptureAnimation(boolean shortFlash) { 1489 mCaptureOverlay.startFlashAnimation(shortFlash); 1490 } 1491 1492 /** 1493 * Cancels the pre-capture animation. 1494 */ cancelPreCaptureAnimation()1495 public void cancelPreCaptureAnimation() { 1496 mAnimationManager.cancelAnimations(); 1497 } 1498 1499 /** 1500 * Cancels the post-capture animation. 1501 */ cancelPostCaptureAnimation()1502 public void cancelPostCaptureAnimation() { 1503 mAnimationManager.cancelAnimations(); 1504 } 1505 getFilmstripContentPanel()1506 public FilmstripContentPanel getFilmstripContentPanel() { 1507 return mFilmstripPanel; 1508 } 1509 1510 /** 1511 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the 1512 * bottom of the filmstrip. 1513 */ getFilmstripBottomControls()1514 public BottomPanel getFilmstripBottomControls() { 1515 return mFilmstripBottomControls; 1516 } 1517 showBottomControls()1518 public void showBottomControls() { 1519 mFilmstripBottomControls.show(); 1520 } 1521 hideBottomControls()1522 public void hideBottomControls() { 1523 mFilmstripBottomControls.hide(); 1524 } 1525 1526 /** 1527 * @param listener The listener for bottom controls. 1528 */ setFilmstripBottomControlsListener(BottomPanel.Listener listener)1529 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) { 1530 mFilmstripBottomControls.setListener(listener); 1531 } 1532 1533 /***************************SurfaceTexture Api and Listener*********************************/ 1534 1535 /** 1536 * Return the shared surface texture. 1537 */ getSurfaceTexture()1538 public SurfaceTexture getSurfaceTexture() { 1539 return mSurface; 1540 } 1541 1542 /** 1543 * Return the shared {@link android.graphics.SurfaceTexture}'s width. 1544 */ getSurfaceWidth()1545 public int getSurfaceWidth() { 1546 return mSurfaceWidth; 1547 } 1548 1549 /** 1550 * Return the shared {@link android.graphics.SurfaceTexture}'s height. 1551 */ getSurfaceHeight()1552 public int getSurfaceHeight() { 1553 return mSurfaceHeight; 1554 } 1555 1556 @Override onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)1557 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 1558 mSurface = surface; 1559 mSurfaceWidth = width; 1560 mSurfaceHeight = height; 1561 Log.v(TAG, "SurfaceTexture is available"); 1562 if (mPreviewStatusListener != null) { 1563 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height); 1564 } 1565 enableModeOptions(); 1566 } 1567 1568 @Override onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)1569 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 1570 mSurface = surface; 1571 mSurfaceWidth = width; 1572 mSurfaceHeight = height; 1573 if (mPreviewStatusListener != null) { 1574 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height); 1575 } 1576 } 1577 1578 @Override onSurfaceTextureDestroyed(SurfaceTexture surface)1579 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 1580 mSurface = null; 1581 Log.v(TAG, "SurfaceTexture is destroyed"); 1582 if (mPreviewStatusListener != null) { 1583 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface); 1584 } 1585 return false; 1586 } 1587 1588 @Override onSurfaceTextureUpdated(SurfaceTexture surface)1589 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 1590 mSurface = surface; 1591 if (mPreviewStatusListener != null) { 1592 mPreviewStatusListener.onSurfaceTextureUpdated(surface); 1593 } 1594 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) { 1595 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated"); 1596 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1597 hideModeCover(); 1598 } 1599 } 1600 1601 /****************************Grid lines api ******************************/ 1602 1603 /** 1604 * Show a set of evenly spaced lines over the preview. The number 1605 * of lines horizontally and vertically is determined by 1606 * {@link com.android.camera.ui.GridLines}. 1607 */ showGridLines()1608 public void showGridLines() { 1609 if (mGridLines != null) { 1610 mGridLines.setVisibility(View.VISIBLE); 1611 } 1612 } 1613 1614 /** 1615 * Hide the set of evenly spaced grid lines overlaying the preview. 1616 */ hideGridLines()1617 public void hideGridLines() { 1618 if (mGridLines != null) { 1619 mGridLines.setVisibility(View.INVISIBLE); 1620 } 1621 } 1622 1623 /** 1624 * Return a callback which shows or hide the preview grid lines 1625 * depending on whether the grid lines setting is set on. 1626 */ getGridLinesCallback()1627 public ButtonManager.ButtonCallback getGridLinesCallback() { 1628 return new ButtonManager.ButtonCallback() { 1629 @Override 1630 public void onStateChanged(int state) { 1631 if (Keys.areGridLinesOn(mController.getSettingsManager())) { 1632 showGridLines(); 1633 } else { 1634 hideGridLines(); 1635 } 1636 } 1637 }; 1638 } 1639 1640 /***************************Mode options api *****************************/ 1641 1642 /** 1643 * Set the mode options visible. 1644 */ 1645 public void showModeOptions() { 1646 /* Make mode options clickable. */ 1647 enableModeOptions(); 1648 mModeOptionsOverlay.setVisibility(View.VISIBLE); 1649 } 1650 1651 /** 1652 * Set the mode options invisible. This is necessary for modes 1653 * that don't show a bottom bar for the capture UI. 1654 */ 1655 public void hideModeOptions() { 1656 mModeOptionsOverlay.setVisibility(View.INVISIBLE); 1657 } 1658 1659 /****************************Bottom bar api ******************************/ 1660 1661 /** 1662 * Sets up the bottom bar and mode options with the correct 1663 * shutter button and visibility based on the current module. 1664 */ 1665 public void resetBottomControls(ModuleController module, int moduleIndex) { 1666 if (areBottomControlsUsed(module)) { 1667 setBottomBarShutterIcon(moduleIndex); 1668 mCaptureLayoutHelper.setShowBottomBar(true); 1669 } else { 1670 mCaptureLayoutHelper.setShowBottomBar(false); 1671 } 1672 } 1673 1674 /** 1675 * Show or hide the mode options and bottom bar, based on 1676 * whether the current module is using the bottom bar. Returns 1677 * whether the mode options and bottom bar are used. 1678 */ 1679 private boolean areBottomControlsUsed(ModuleController module) { 1680 if (module.isUsingBottomBar()) { 1681 showBottomBar(); 1682 showModeOptions(); 1683 return true; 1684 } else { 1685 hideBottomBar(); 1686 hideModeOptions(); 1687 return false; 1688 } 1689 } 1690 1691 /** 1692 * Set the bottom bar visible. 1693 */ 1694 public void showBottomBar() { 1695 mBottomBar.setVisibility(View.VISIBLE); 1696 } 1697 1698 /** 1699 * Set the bottom bar invisible. 1700 */ 1701 public void hideBottomBar() { 1702 mBottomBar.setVisibility(View.INVISIBLE); 1703 } 1704 1705 /** 1706 * Sets the color of the bottom bar. 1707 */ 1708 public void setBottomBarColor(int colorId) { 1709 mBottomBar.setBackgroundColor(colorId); 1710 } 1711 1712 /** 1713 * Sets the pressed color of the bottom bar for a camera mode index. 1714 */ 1715 public void setBottomBarColorsForModeIndex(int index) { 1716 mBottomBar.setColorsForModeIndex(index); 1717 } 1718 1719 /** 1720 * Sets the shutter button icon on the bottom bar, based on 1721 * the mode index. 1722 */ 1723 public void setBottomBarShutterIcon(int modeIndex) { 1724 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex, 1725 mController.getAndroidContext()); 1726 mBottomBar.setShutterButtonIcon(shutterIconId); 1727 } 1728 1729 public void animateBottomBarToVideoStop(int shutterIconId) { 1730 mBottomBar.animateToVideoStop(shutterIconId); 1731 } 1732 1733 public void animateBottomBarToFullSize(int shutterIconId) { 1734 mBottomBar.animateToFullSize(shutterIconId); 1735 } 1736 1737 public void setShutterButtonEnabled(final boolean enabled) { 1738 if (!mDisableAllUserInteractions) { 1739 mBottomBar.post(new Runnable() { 1740 @Override 1741 public void run() { 1742 mBottomBar.setShutterButtonEnabled(enabled); 1743 } 1744 }); 1745 } 1746 } 1747 1748 public void setShutterButtonImportantToA11y(boolean important) { 1749 mBottomBar.setShutterButtonImportantToA11y(important); 1750 } 1751 1752 public boolean isShutterButtonEnabled() { 1753 return mBottomBar.isShutterButtonEnabled(); 1754 } 1755 1756 public void setIndicatorBottomBarWrapperVisible(boolean visible) { 1757 mIndicatorBottomBarWrapper.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1758 } 1759 1760 /** 1761 * Set the visibility of the bottom bar. 1762 */ 1763 // TODO: needed for when panorama is managed by the generic module ui. 1764 public void setBottomBarVisible(boolean visible) { 1765 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1766 } 1767 1768 /** 1769 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button. 1770 */ 1771 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) { 1772 mShutterButton.addOnShutterButtonListener(listener); 1773 } 1774 1775 /** 1776 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button. 1777 */ 1778 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) { 1779 mShutterButton.removeOnShutterButtonListener(listener); 1780 } 1781 1782 /** 1783 * Performs a transition to the capture layout of the bottom bar. 1784 */ 1785 public void transitionToCapture() { 1786 ModuleController moduleController = mController.getCurrentModuleController(); 1787 applyModuleSpecs(moduleController.getHardwareSpec(), 1788 moduleController.getBottomBarSpec()); 1789 mBottomBar.transitionToCapture(); 1790 } 1791 1792 /** 1793 * Displays the Cancel button instead of the capture button. 1794 */ 1795 public void transitionToCancel() { 1796 ModuleController moduleController = mController.getCurrentModuleController(); 1797 applyModuleSpecs(moduleController.getHardwareSpec(), 1798 moduleController.getBottomBarSpec()); 1799 mBottomBar.transitionToCancel(); 1800 } 1801 1802 /** 1803 * Performs a transition to the global intent layout. 1804 */ 1805 public void transitionToIntentCaptureLayout() { 1806 ModuleController moduleController = mController.getCurrentModuleController(); 1807 applyModuleSpecs(moduleController.getHardwareSpec(), 1808 moduleController.getBottomBarSpec()); 1809 mBottomBar.transitionToIntentCaptureLayout(); 1810 } 1811 1812 /** 1813 * Performs a transition to the global intent review layout. 1814 */ 1815 public void transitionToIntentReviewLayout() { 1816 ModuleController moduleController = mController.getCurrentModuleController(); 1817 applyModuleSpecs(moduleController.getHardwareSpec(), 1818 moduleController.getBottomBarSpec()); 1819 mBottomBar.transitionToIntentReviewLayout(); 1820 } 1821 1822 /** 1823 * @return whether UI is in intent review mode 1824 */ 1825 public boolean isInIntentReview() { 1826 return mBottomBar.isInIntentReview(); 1827 } 1828 1829 @Override 1830 public void onSettingChanged(SettingsManager settingsManager, String key) { 1831 // Update the mode options based on the hardware spec, 1832 // when hdr changes to prevent flash from getting out of sync. 1833 if (key.equals(Keys.KEY_CAMERA_HDR)) { 1834 ModuleController moduleController = mController.getCurrentModuleController(); 1835 applyModuleSpecs(moduleController.getHardwareSpec(), 1836 moduleController.getBottomBarSpec()); 1837 } 1838 } 1839 1840 /** 1841 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec} 1842 * to the bottom bar mode options based on limitations from a 1843 * {@link com.android.camera.hardware.HardwareSpec}. 1844 * 1845 * Options not supported by the hardware are either hidden 1846 * or disabled, depending on the option. 1847 * 1848 * Otherwise, the option is fully enabled and clickable. 1849 */ 1850 public void applyModuleSpecs(final HardwareSpec hardwareSpec, 1851 final BottomBarUISpec bottomBarSpec) { 1852 if (hardwareSpec == null || bottomBarSpec == null) { 1853 return; 1854 } 1855 1856 ButtonManager buttonManager = mController.getButtonManager(); 1857 SettingsManager settingsManager = mController.getSettingsManager(); 1858 1859 buttonManager.setToInitialState(); 1860 1861 /** Standard mode options */ 1862 if (mController.getCameraProvider().getNumberOfCameras() > 1 && 1863 hardwareSpec.isFrontCameraSupported()) { 1864 if (bottomBarSpec.enableCamera) { 1865 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA, 1866 bottomBarSpec.cameraCallback); 1867 } else { 1868 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA); 1869 } 1870 } else { 1871 // Hide camera icon if front camera not available. 1872 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA); 1873 } 1874 1875 boolean flashBackCamera = mController.getSettingsManager().getBoolean( 1876 SettingsManager.SCOPE_GLOBAL, Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA); 1877 if (bottomBarSpec.hideFlash || !flashBackCamera) { 1878 // Hide both flash and torch button in flash disable logic 1879 buttonManager.hideButton(ButtonManager.BUTTON_FLASH); 1880 buttonManager.hideButton(ButtonManager.BUTTON_TORCH); 1881 } else { 1882 if (hardwareSpec.isFlashSupported()) { 1883 if (bottomBarSpec.enableFlash) { 1884 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH, 1885 bottomBarSpec.flashCallback); 1886 } else if (bottomBarSpec.enableTorchFlash) { 1887 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH, 1888 bottomBarSpec.flashCallback); 1889 } else if (bottomBarSpec.enableHdrPlusFlash) { 1890 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH, 1891 bottomBarSpec.flashCallback); 1892 } else { 1893 // Hide both flash and torch button in flash disable logic 1894 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1895 buttonManager.disableButton(ButtonManager.BUTTON_TORCH); 1896 } 1897 } else { 1898 // Disable both flash and torch icon if not supported 1899 // by the chosen camera hardware. 1900 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1901 buttonManager.disableButton(ButtonManager.BUTTON_TORCH); 1902 } 1903 } 1904 1905 if (bottomBarSpec.hideHdr || mIsCaptureIntent) { 1906 // Force hide hdr or hdr plus icon. 1907 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS); 1908 } else { 1909 if (hardwareSpec.isHdrPlusSupported()) { 1910 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager, 1911 mController.getModuleScope())) { 1912 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS, 1913 bottomBarSpec.hdrCallback); 1914 } else { 1915 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS); 1916 } 1917 } else if (hardwareSpec.isHdrSupported()) { 1918 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager, 1919 mController.getModuleScope())) { 1920 buttonManager.initializeButton(ButtonManager.BUTTON_HDR, 1921 bottomBarSpec.hdrCallback); 1922 } else { 1923 buttonManager.disableButton(ButtonManager.BUTTON_HDR); 1924 } 1925 } else { 1926 // Hide hdr plus or hdr icon if neither are supported. 1927 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS); 1928 } 1929 } 1930 1931 if (bottomBarSpec.hideGridLines) { 1932 // Force hide grid lines icon. 1933 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES); 1934 hideGridLines(); 1935 } else { 1936 if (bottomBarSpec.enableGridLines) { 1937 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES, 1938 bottomBarSpec.gridLinesCallback != null ? 1939 bottomBarSpec.gridLinesCallback : getGridLinesCallback() 1940 ); 1941 } else { 1942 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES); 1943 hideGridLines(); 1944 } 1945 } 1946 1947 if (bottomBarSpec.enableSelfTimer) { 1948 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null); 1949 } else { 1950 if (bottomBarSpec.showSelfTimer) { 1951 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN); 1952 } else { 1953 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN); 1954 } 1955 } 1956 1957 if (bottomBarSpec.enablePanoOrientation 1958 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) { 1959 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback); 1960 } 1961 1962 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation && 1963 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) && 1964 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL, 1965 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED); 1966 if (enableExposureCompensation) { 1967 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null); 1968 buttonManager.setExposureCompensationParameters( 1969 bottomBarSpec.minExposureCompensation, 1970 bottomBarSpec.maxExposureCompensation, 1971 bottomBarSpec.exposureCompensationStep); 1972 1973 buttonManager.setExposureCompensationCallback( 1974 bottomBarSpec.exposureCompensationSetCallback); 1975 buttonManager.updateExposureButtons(); 1976 } else { 1977 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION); 1978 buttonManager.setExposureCompensationCallback(null); 1979 } 1980 1981 /** Intent UI */ 1982 if (bottomBarSpec.showCancel) { 1983 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL, 1984 bottomBarSpec.cancelCallback); 1985 } 1986 if (bottomBarSpec.showDone) { 1987 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE, 1988 bottomBarSpec.doneCallback); 1989 } 1990 if (bottomBarSpec.showRetake) { 1991 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE, 1992 bottomBarSpec.retakeCallback); 1993 } 1994 if (bottomBarSpec.showReview) { 1995 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW, 1996 bottomBarSpec.reviewCallback, 1997 R.drawable.ic_play); 1998 } 1999 } 2000 2001 /** 2002 * Shows the given tutorial on the screen. 2003 */ 2004 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) { 2005 tutorial.show(mTutorialsPlaceHolderWrapper, inflater); 2006 } 2007 2008 /***************************Filmstrip api *****************************/ 2009 2010 public void showFilmstrip() { 2011 mModeListView.onBackPressed(); 2012 mFilmstripLayout.showFilmstrip(); 2013 } 2014 2015 public void hideFilmstrip() { 2016 mFilmstripLayout.hideFilmstrip(); 2017 } 2018 } 2019