CameraAppUI.java revision 94c9b43061bee072a2322a2001d27e51dae023db
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.camera.app; 18 19import android.content.Context; 20import android.graphics.Bitmap; 21import android.graphics.Canvas; 22import android.graphics.Matrix; 23import android.graphics.RectF; 24import android.graphics.SurfaceTexture; 25import android.hardware.display.DisplayManager; 26import android.util.Log; 27import android.view.GestureDetector; 28import android.view.LayoutInflater; 29import android.view.MotionEvent; 30import android.view.TextureView; 31import android.view.View; 32import android.view.ViewConfiguration; 33import android.view.ViewGroup; 34import android.widget.FrameLayout; 35 36import com.android.camera.AnimationManager; 37import com.android.camera.ButtonManager; 38import com.android.camera.ShutterButton; 39import com.android.camera.TextureViewHelper; 40import com.android.camera.filmstrip.FilmstripContentPanel; 41import com.android.camera.hardware.HardwareSpec; 42import com.android.camera.module.ModuleController; 43import com.android.camera.settings.SettingsManager; 44import com.android.camera.ui.BottomBar; 45import com.android.camera.ui.CaptureAnimationOverlay; 46import com.android.camera.ui.GridLines; 47import com.android.camera.ui.MainActivityLayout; 48import com.android.camera.ui.ModeListView; 49import com.android.camera.ui.ModeTransitionView; 50import com.android.camera.ui.PreviewOverlay; 51import com.android.camera.ui.PreviewStatusListener; 52import com.android.camera.util.ApiHelper; 53import android.util.CameraPerformanceTracker; 54import com.android.camera.util.CameraUtil; 55import com.android.camera.util.PhotoSphereHelper; 56import com.android.camera.util.UsageStatistics; 57import com.android.camera.widget.FilmstripLayout; 58import com.android.camera.widget.IndicatorIconController; 59import com.android.camera.widget.ModeOptionsOverlay; 60import com.android.camera2.R; 61import com.google.common.logging.eventprotos; 62 63/** 64 * CameraAppUI centralizes control of views shared across modules. Whereas module 65 * specific views will be handled in each Module UI. For example, we can now 66 * bring the flash animation and capture animation up from each module to app 67 * level, as these animations are largely the same for all modules. 68 * 69 * This class also serves to disambiguate touch events. It recognizes all the 70 * swipe gestures that happen on the preview by attaching a touch listener to 71 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge 72 * of how swipe from each direction should be handled, it can then redirect these 73 * events to appropriate recipient views. 74 */ 75public class CameraAppUI implements ModeListView.ModeSwitchListener, 76 TextureView.SurfaceTextureListener, ModeListView.ModeListOpenListener { 77 78 /** 79 * The bottom controls on the filmstrip. 80 */ 81 public static interface BottomControls { 82 /** Values for the view state of the button. */ 83 public final int VIEWER_NONE = 0; 84 public final int VIEWER_PHOTO_SPHERE = 1; 85 public final int VIEWER_REFOCUS = 2; 86 public final int VIEWER_OTHER = 3; 87 88 /** 89 * Sets a new or replaces an existing listener for bottom control events. 90 */ 91 void setListener(Listener listener); 92 93 /** 94 * Set if the bottom controls are visible. 95 * @param visible {@code true} if visible. 96 */ 97 void setVisible(boolean visible); 98 99 /** 100 * @param visible Whether the button is visible. 101 */ 102 void setEditButtonVisibility(boolean visible); 103 104 /** 105 * @param enabled Whether the button is enabled. 106 */ 107 void setEditEnabled(boolean enabled); 108 109 /** 110 * Sets the visibility of the view-photosphere button. 111 * 112 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE}, 113 * {@link #VIEWER_REFOCUS}. 114 */ 115 void setViewerButtonVisibility(int state); 116 117 /** 118 * @param enabled Whether the button is enabled. 119 */ 120 void setViewEnabled(boolean enabled); 121 122 /** 123 * @param enabled Whether the button is enabled. 124 */ 125 void setTinyPlanetEnabled(boolean enabled); 126 127 /** 128 * @param visible Whether the button is visible. 129 */ 130 void setDeleteButtonVisibility(boolean visible); 131 132 /** 133 * @param enabled Whether the button is enabled. 134 */ 135 void setDeleteEnabled(boolean enabled); 136 137 /** 138 * @param visible Whether the button is visible. 139 */ 140 void setShareButtonVisibility(boolean visible); 141 142 /** 143 * @param enabled Whether the button is enabled. 144 */ 145 void setShareEnabled(boolean enabled); 146 147 /** 148 * Classes implementing this interface can listen for events on the bottom 149 * controls. 150 */ 151 public static interface Listener { 152 /** 153 * Called when the user pressed the "view" button to e.g. view a photo 154 * sphere or RGBZ image. 155 */ 156 public void onExternalViewer(); 157 158 /** 159 * Called when the "edit" button is pressed. 160 */ 161 public void onEdit(); 162 163 /** 164 * Called when the "tiny planet" button is pressed. 165 */ 166 public void onTinyPlanet(); 167 168 /** 169 * Called when the "delete" button is pressed. 170 */ 171 public void onDelete(); 172 173 /** 174 * Called when the "share" button is pressed. 175 */ 176 public void onShare(); 177 } 178 } 179 180 /** 181 * BottomBarUISpec provides a structure for modules 182 * to specify their ideal bottom bar mode options layout. 183 * 184 * Once constructed by a module, this class should be 185 * treated as read only. 186 * 187 * The application then edits this spec according to 188 * hardware limitations and displays the final bottom 189 * bar ui. 190 */ 191 public static class BottomBarUISpec { 192 /** Mode options UI */ 193 194 /** 195 * Set true if the camera option should be enabled. 196 * If not set or false, and multiple cameras are supported, 197 * the camera option will be disabled. 198 * 199 * If multiple cameras are not supported, this preference 200 * is ignored and the camera option will not be visible. 201 */ 202 public boolean enableCamera; 203 204 /** 205 * Set true if the photo flash option should be enabled. 206 * If not set or false, the photo flash option will be 207 * disabled. 208 * 209 * If the hardware does not support multiple flash values, 210 * this preference is ignored and the flash option will 211 * be disabled. It will not be made invisible in order to 212 * preserve a consistent experience across devices and between 213 * front and back cameras. 214 */ 215 public boolean enableFlash; 216 217 /** 218 * Set true if the video flash option should be enabled. 219 * Same disable rules apply as the photo flash option. 220 */ 221 public boolean enableTorchFlash; 222 223 /** 224 * Set true if flash should not be visible, regardless of 225 * hardware limitations. 226 */ 227 public boolean hideFlash; 228 229 /** 230 * Set true if the hdr/hdr+ option should be enabled. 231 * If not set or false, the hdr/hdr+ option will be disabled. 232 * 233 * Hdr or hdr+ will be chosen based on hardware limitations, 234 * with hdr+ prefered. 235 * 236 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+ 237 * will not be visible. 238 */ 239 public boolean enableHdr; 240 241 /** 242 * Set true if hdr/hdr+ should not be visible, regardless of 243 * hardware limitations. 244 */ 245 public boolean hideHdr; 246 247 /** 248 * Set true if grid lines should be visible. Not setting this 249 * causes grid lines to be disabled. This option is agnostic to 250 * the hardware. 251 */ 252 public boolean enableGridLines; 253 254 /** 255 * Set true if grid lines should not be visible. 256 */ 257 public boolean hideGridLines; 258 259 /** 260 * Set true if the panorama horizontal option should be visible. 261 * 262 * This option is not constrained by hardware limitations. 263 */ 264 public boolean enablePanoHorizontal; 265 266 /** 267 * Set true if the panorama vertical option should be visible. 268 * 269 * This option is not constrained by hardware limitations. 270 */ 271 public boolean enablePanoVertical; 272 273 /** Intent UI */ 274 275 /** 276 * Set true if the intent ui cancel option should be visible. 277 */ 278 public boolean showCancel; 279 /** 280 * Set true if the intent ui done option should be visible. 281 */ 282 public boolean showDone; 283 /** 284 * Set true if the intent ui retake option should be visible. 285 */ 286 public boolean showRetake; 287 /** 288 * Set true if the intent ui review option should be visible. 289 */ 290 public boolean showReview; 291 292 /** Mode options callbacks */ 293 294 /** 295 * A {@link android.com.android.camera.ButtonManager.ButtonCallback} 296 * that will be executed when the camera option is pressed. This 297 * callback can be null. 298 */ 299 public ButtonManager.ButtonCallback cameraCallback; 300 301 /** 302 * A {@link android.com.android.camera.ButtonManager.ButtonCallback} 303 * that will be executed when the flash option is pressed. This 304 * callback can be null. 305 */ 306 public ButtonManager.ButtonCallback flashCallback; 307 308 /** 309 * A {@link android.com.android.camera.ButtonManager.ButtonCallback} 310 * that will be executed when the hdr/hdr+ option is pressed. This 311 * callback can be null. 312 */ 313 public ButtonManager.ButtonCallback hdrCallback; 314 315 /** 316 * A {@link android.com.android.camera.ButtonManager.ButtonCallback} 317 * that will be executed when the grid lines option is pressed. This 318 * callback can be null. 319 */ 320 public ButtonManager.ButtonCallback gridLinesCallback; 321 322 /** 323 * A {@link android.view.View.OnClickListener} that will execute 324 * when the panorama horizontal option is pressed. 325 * This callback can be null. 326 */ 327 public View.OnClickListener panoHorizontalCallback; 328 329 /** 330 * A {@link android.view.View.OnClickListener} that will execute 331 * when the panorama vertical option is pressed. 332 * This callback can be null. 333 */ 334 public View.OnClickListener panoVerticalCallback; 335 336 /** Intent UI callbacks */ 337 338 /** 339 * A {@link android.view.View.OnClickListener} that will execute 340 * when the cancel option is pressed. This callback can be null. 341 */ 342 public View.OnClickListener cancelCallback; 343 344 /** 345 * A {@link android.view.View.OnClickListener} that will execute 346 * when the done option is pressed. This callback can be null. 347 */ 348 public View.OnClickListener doneCallback; 349 350 /** 351 * A {@link android.view.View.OnClickListener} that will execute 352 * when the retake option is pressed. This callback can be null. 353 */ 354 public View.OnClickListener retakeCallback; 355 356 /** 357 * A {@link android.view.View.OnClickListener} that will execute 358 * when the review option is pressed. This callback can be null. 359 */ 360 public View.OnClickListener reviewCallback; 361 } 362 363 364 private final static String TAG = "CameraAppUI"; 365 366 private final AppController mController; 367 private final boolean mIsCaptureIntent; 368 private final AnimationManager mAnimationManager; 369 370 // Swipe states: 371 private final static int IDLE = 0; 372 private final static int SWIPE_UP = 1; 373 private final static int SWIPE_DOWN = 2; 374 private final static int SWIPE_LEFT = 3; 375 private final static int SWIPE_RIGHT = 4; 376 private boolean mSwipeEnabled = true; 377 378 // Shared Surface Texture properities. 379 private SurfaceTexture mSurface; 380 private int mSurfaceWidth; 381 private int mSurfaceHeight; 382 383 // Touch related measures: 384 private final int mSlop; 385 private final static int SWIPE_TIME_OUT_MS = 500; 386 387 private final static int SHIMMY_DELAY_MS = 1000; 388 389 // Mode cover states: 390 private final static int COVER_HIDDEN = 0; 391 private final static int COVER_SHOWN = 1; 392 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2; 393 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3; 394 395 // App level views: 396 private final FrameLayout mCameraRootView; 397 private final ModeTransitionView mModeTransitionView; 398 private final MainActivityLayout mAppRootView; 399 private final ModeListView mModeListView; 400 private final FilmstripLayout mFilmstripLayout; 401 private TextureView mTextureView; 402 private FrameLayout mModuleUI; 403 private BottomBar mBottomBar; 404 private ModeOptionsOverlay mModeOptionsOverlay; 405 private boolean mShouldShowShimmy = false; 406 private IndicatorIconController mIndicatorIconController; 407 408 private TextureViewHelper mTextureViewHelper; 409 private final GestureDetector mGestureDetector; 410 private DisplayManager.DisplayListener mDisplayListener; 411 private int mLastRotation; 412 private int mSwipeState = IDLE; 413 private PreviewOverlay mPreviewOverlay; 414 private GridLines mGridLines; 415 private CaptureAnimationOverlay mCaptureOverlay; 416 private PreviewStatusListener mPreviewStatusListener; 417 private int mModeCoverState = COVER_HIDDEN; 418 private final FilmstripBottomControls mFilmstripBottomControls; 419 private final FilmstripContentPanel mFilmstripPanel; 420 private Runnable mHideCoverRunnable; 421 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener 422 = new View.OnLayoutChangeListener() { 423 @Override 424 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 425 int oldTop, int oldRight, int oldBottom) { 426 if (mPreviewStatusListener != null) { 427 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft, 428 oldTop, oldRight, oldBottom); 429 } 430 } 431 }; 432 433 /** 434 * Provides current preview frame and the controls/overlay from the module that 435 * are shown on top of the preview. 436 */ 437 public interface CameraModuleScreenShotProvider { 438 /** 439 * Returns the current preview frame down-sampled using the given down-sample 440 * factor. 441 * 442 * @param downSampleFactor the down sample factor for down sampling the 443 * preview frame. (e.g. a down sample factor of 444 * 2 means to scale down the preview frame to 1/2 445 * the width and height.) 446 * @return down-sampled preview frame 447 */ 448 public Bitmap getPreviewFrame(int downSampleFactor); 449 450 /** 451 * @return the controls and overlays that are currently showing on top of 452 * the preview drawn into a bitmap with no scaling applied. 453 */ 454 public Bitmap getPreviewOverlayAndControls(); 455 } 456 457 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider = 458 new CameraModuleScreenShotProvider() { 459 @Override 460 public Bitmap getPreviewFrame(int downSampleFactor) { 461 if (mCameraRootView == null || mTextureView == null) { 462 return null; 463 } 464 465 RectF previewArea = mTextureViewHelper.getPreviewArea(); 466 // Gets the bitmap from the preview TextureView. 467 Bitmap preview = mTextureView.getBitmap( 468 (int) previewArea.width() / downSampleFactor, 469 (int) previewArea.height() / downSampleFactor); 470 return preview; 471 } 472 473 @Override 474 public Bitmap getPreviewOverlayAndControls() { 475 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(), 476 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888); 477 Canvas canvas = new Canvas(overlays); 478 mCameraRootView.draw(canvas); 479 return overlays; 480 } 481 }; 482 483 private long mCoverHiddenTime = -1; // System time when preview cover was hidden. 484 485 public long getCoverHiddenTime() { 486 return mCoverHiddenTime; 487 } 488 489 /** 490 * This resets the preview to have no applied transform matrix. 491 */ 492 public void clearPreviewTransform() { 493 mTextureViewHelper.clearTransform(); 494 } 495 496 public void updatePreviewAspectRatio(float aspectRatio) { 497 mTextureViewHelper.updateAspectRatio(aspectRatio); 498 } 499 500 /** 501 * This is to support modules that calculate their own transform matrix because 502 * they need to use a transform matrix to rotate the preview. 503 * 504 * @param matrix transform matrix to be set on the TextureView 505 */ 506 public void updatePreviewTransform(Matrix matrix) { 507 mTextureViewHelper.updateTransform(matrix); 508 } 509 510 public interface AnimationFinishedListener { 511 public void onAnimationFinished(boolean success); 512 } 513 514 private class MyTouchListener implements View.OnTouchListener { 515 private boolean mScaleStarted = false; 516 @Override 517 public boolean onTouch(View v, MotionEvent event) { 518 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 519 mScaleStarted = false; 520 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { 521 mScaleStarted = true; 522 } 523 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event); 524 } 525 } 526 527 /** 528 * This gesture listener finds out the direction of the scroll gestures and 529 * sends them to CameraAppUI to do further handling. 530 */ 531 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener { 532 private MotionEvent mDown; 533 534 @Override 535 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) { 536 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS 537 || mSwipeState != IDLE 538 || mIsCaptureIntent 539 || !mSwipeEnabled) { 540 return false; 541 } 542 543 int deltaX = (int) (ev.getX() - mDown.getX()); 544 int deltaY = (int) (ev.getY() - mDown.getY()); 545 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { 546 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) { 547 // Calculate the direction of the swipe. 548 if (deltaX >= Math.abs(deltaY)) { 549 // Swipe right. 550 setSwipeState(SWIPE_RIGHT); 551 } else if (deltaX <= -Math.abs(deltaY)) { 552 // Swipe left. 553 setSwipeState(SWIPE_LEFT); 554 } else if (deltaY >= Math.abs(deltaX)) { 555 // Swipe down. 556 setSwipeState(SWIPE_DOWN); 557 } else if (deltaY <= -Math.abs(deltaX)) { 558 // Swipe up. 559 setSwipeState(SWIPE_UP); 560 } 561 } 562 } 563 return true; 564 } 565 566 private void setSwipeState(int swipeState) { 567 mSwipeState = swipeState; 568 // Notify new swipe detected. 569 onSwipeDetected(swipeState); 570 } 571 572 @Override 573 public boolean onDown(MotionEvent ev) { 574 mDown = MotionEvent.obtain(ev); 575 mSwipeState = IDLE; 576 return false; 577 } 578 } 579 580 public CameraAppUI(AppController controller, MainActivityLayout appRootView, 581 boolean isCaptureIntent) { 582 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop(); 583 mController = controller; 584 mIsCaptureIntent = isCaptureIntent; 585 586 mAppRootView = appRootView; 587 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout); 588 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root); 589 mModeTransitionView = (ModeTransitionView) 590 mAppRootView.findViewById(R.id.mode_transition_view); 591 mFilmstripBottomControls = new FilmstripBottomControls(controller, 592 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_controls)); 593 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout); 594 mGestureDetector = new GestureDetector(controller.getAndroidContext(), 595 new MyGestureListener()); 596 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout); 597 if (mModeListView != null) { 598 mModeListView.setModeSwitchListener(this); 599 mModeListView.setModeListOpenListener(this); 600 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider); 601 } else { 602 Log.e(TAG, "Cannot find mode list in the view hierarchy"); 603 } 604 mAnimationManager = new AnimationManager(); 605 initDisplayListener(); 606 } 607 608 /** 609 * Enable or disable swipe gestures. We want to disable them e.g. while we 610 * record a video. 611 */ 612 public void setSwipeEnabled(boolean enabled) { 613 mAppRootView.setSwipeEnabled(enabled); 614 mSwipeEnabled = enabled; 615 } 616 617 public void onDestroy() { 618 ((DisplayManager) mController.getAndroidContext() 619 .getSystemService(Context.DISPLAY_SERVICE)) 620 .unregisterDisplayListener(mDisplayListener); 621 } 622 623 /** 624 * Initializes the display listener to listen to display changes such as 625 * 180-degree rotation change, which will not have an onConfigurationChanged 626 * callback. 627 */ 628 private void initDisplayListener() { 629 if (ApiHelper.HAS_DISPLAY_LISTENER) { 630 mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext()); 631 632 mDisplayListener = new DisplayManager.DisplayListener() { 633 @Override 634 public void onDisplayAdded(int arg0) { 635 // Do nothing. 636 } 637 638 @Override 639 public void onDisplayChanged(int displayId) { 640 int rotation = CameraUtil.getDisplayRotation( 641 mController.getAndroidContext()); 642 if ((rotation - mLastRotation + 360) % 360 == 180 643 && mPreviewStatusListener != null) { 644 mPreviewStatusListener.onPreviewFlipped(); 645 } 646 mLastRotation = rotation; 647 } 648 649 @Override 650 public void onDisplayRemoved(int arg0) { 651 // Do nothing. 652 } 653 }; 654 655 ((DisplayManager) mController.getAndroidContext() 656 .getSystemService(Context.DISPLAY_SERVICE)) 657 .registerDisplayListener(mDisplayListener, null); 658 } 659 } 660 661 /** 662 * Redirects touch events to appropriate recipient views based on swipe direction. 663 * More specifically, swipe up and swipe down will be handled by the view that handles 664 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected 665 * to mode list in order to bring up mode list. 666 */ 667 private void onSwipeDetected(int swipeState) { 668 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) { 669 // Quick switch between modes. 670 int currentModuleIndex = mController.getCurrentModuleIndex(); 671 final int moduleToTransitionTo = 672 mController.getQuickSwitchToModuleId(currentModuleIndex); 673 if (currentModuleIndex != moduleToTransitionTo) { 674 mAppRootView.redirectTouchEventsTo(mModeTransitionView); 675 676 int shadeColorId = CameraUtil.getCameraThemeColorId(moduleToTransitionTo, 677 mController.getAndroidContext()); 678 int iconRes = CameraUtil.getCameraModeIconResId(moduleToTransitionTo, 679 mController.getAndroidContext()); 680 681 AnimationFinishedListener listener = new AnimationFinishedListener() { 682 @Override 683 public void onAnimationFinished(boolean success) { 684 if (success) { 685 mHideCoverRunnable = new Runnable() { 686 @Override 687 public void run() { 688 mModeTransitionView.startPeepHoleAnimation(); 689 } 690 }; 691 mModeCoverState = COVER_SHOWN; 692 // Go to new module when the previous operation is successful. 693 mController.onModeSelected(moduleToTransitionTo); 694 } 695 } 696 }; 697 if (mSwipeState == SWIPE_UP) { 698 mModeTransitionView.prepareToPullUpShade(shadeColorId, iconRes, listener); 699 } else { 700 mModeTransitionView.prepareToPullDownShade(shadeColorId, iconRes, listener); 701 } 702 } 703 } else if (swipeState == SWIPE_LEFT) { 704 // Pass the touch sequence to filmstrip layout. 705 UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.FILMSTRIP, 706 eventprotos.CameraEvent.InteractionCause.SWIPE_LEFT); 707 mAppRootView.redirectTouchEventsTo(mFilmstripLayout); 708 } else if (swipeState == SWIPE_RIGHT) { 709 // Pass the touch to mode switcher 710 mAppRootView.redirectTouchEventsTo(mModeListView); 711 } 712 } 713 714 /** 715 * Gets called when activity resumes in preview. 716 */ 717 public void resume() { 718 if (mTextureView == null || mTextureView.getSurfaceTexture() != null) { 719 if (!mIsCaptureIntent) { 720 showShimmyDelayed(); 721 } 722 } else { 723 // Show mode theme cover until preview is ready 724 showModeCoverUntilPreviewReady(); 725 } 726 // Hide action bar first since we are in full screen mode first, and 727 // switch the system UI to lights-out mode. 728 mFilmstripPanel.hide(); 729 } 730 731 /** 732 * A cover view showing the mode theme color and mode icon will be visible on 733 * top of preview until preview is ready (i.e. camera preview is started and 734 * the first frame has been received). 735 */ 736 private void showModeCoverUntilPreviewReady() { 737 int modeId = mController.getCurrentModuleIndex(); 738 int colorId = CameraUtil.getCameraThemeColorId(modeId, mController.getAndroidContext()); 739 int iconId = CameraUtil.getCameraModeIconResId(modeId, mController.getAndroidContext()); 740 mModeTransitionView.setupModeCover(colorId, iconId); 741 mHideCoverRunnable = new Runnable() { 742 @Override 743 public void run() { 744 mModeTransitionView.hideModeCover(new AnimationFinishedListener() { 745 @Override 746 public void onAnimationFinished(boolean success) { 747 if (success) { 748 showShimmyDelayed(); 749 } 750 } 751 }); 752 } 753 }; 754 mModeCoverState = COVER_SHOWN; 755 } 756 757 private void showShimmyDelayed() { 758 if (!mIsCaptureIntent) { 759 // Show shimmy in SHIMMY_DELAY_MS 760 mShouldShowShimmy = mController.shouldShowShimmy(); 761 if (mShouldShowShimmy) { 762 mModeListView.startAccordionAnimationWithDelay(SHIMMY_DELAY_MS); 763 } 764 } 765 } 766 767 private void hideModeCover() { 768 if (mHideCoverRunnable != null) { 769 mAppRootView.post(mHideCoverRunnable); 770 mHideCoverRunnable = null; 771 } 772 mModeCoverState = COVER_HIDDEN; 773 if (mCoverHiddenTime < 0) { 774 mCoverHiddenTime = System.currentTimeMillis(); 775 } 776 } 777 778 @Override 779 public void onOpenFullScreen() { 780 if (mShouldShowShimmy) { 781 mController.decrementShimmyPlayTimes(); 782 // Sets should show shimmy flag to false for this session (i.e. until 783 // next onResume) 784 mShouldShowShimmy = false; 785 } 786 } 787 788 /** 789 * Called when the back key is pressed. 790 * 791 * @return Whether the UI responded to the key event. 792 */ 793 public boolean onBackPressed() { 794 if (mFilmstripLayout.getVisibility() == View.VISIBLE) { 795 return mFilmstripLayout.onBackPressed(); 796 } else { 797 return mModeListView.onBackPressed(); 798 } 799 } 800 801 /** 802 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that 803 * listens to SurfaceTexture changes. In addition, listeners are set on 804 * dependent app ui elements. 805 * 806 * @param previewStatusListener the listener that gets notified when SurfaceTexture 807 * changes 808 */ 809 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) { 810 mPreviewStatusListener = previewStatusListener; 811 if (mPreviewStatusListener != null) { 812 onPreviewListenerChanged(); 813 } 814 } 815 816 /** 817 * When the PreviewStatusListener changes, listeners need to be 818 * set on the following app ui elements: 819 * {@link com.android.camera.ui.PreviewOverlay}, 820 * {@link com.android.camera.ui.BottomBar}, 821 * {@link com.android.camera.ui.IndicatorIconController}. 822 */ 823 private void onPreviewListenerChanged() { 824 // Set a listener for recognizing preview gestures. 825 GestureDetector.OnGestureListener gestureListener 826 = mPreviewStatusListener.getGestureListener(); 827 if (gestureListener != null) { 828 mPreviewOverlay.setGestureListener(gestureListener); 829 } 830 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener(); 831 if (touchListener != null) { 832 mPreviewOverlay.setTouchListener(touchListener); 833 } 834 835 mTextureViewHelper.setAutoAdjustTransform( 836 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout()); 837 838 if (mPreviewStatusListener.shouldAutoAdjustBottomBar()) { 839 mTextureViewHelper.addPreviewAreaSizeChangedListener(mBottomBar); 840 841 mBottomBar.setAdjustPreviewAreaListener(new BottomBar.AdjustPreviewAreaListener() { 842 @Override 843 public void centerPreviewAreaInRect(RectF rect) { 844 mTextureViewHelper.centerPreviewInRect(rect); 845 } 846 }); 847 } 848 } 849 850 /** 851 * This method should be called in onCameraOpened. It defines CameraAppUI 852 * specific changes that depend on the camera or camera settings. 853 */ 854 public void onChangeCamera() { 855 ModuleController moduleController = mController.getCurrentModuleController(); 856 applyModuleSpecs(moduleController.getHardwareSpec(), 857 moduleController.getBottomBarSpec()); 858 859 if (mIndicatorIconController != null) { 860 // Sync the settings state with the indicator state. 861 mIndicatorIconController.syncIndicators(); 862 } 863 } 864 865 /** 866 * Adds a listener to receive callbacks when preview area size changes. 867 */ 868 public void addPreviewAreaSizeChangedListener( 869 PreviewStatusListener.PreviewAreaSizeChangedListener listener) { 870 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener); 871 } 872 873 /** 874 * Removes a listener that receives callbacks when preview area size changes. 875 */ 876 public void removePreviewAreaSizeChangedListener( 877 PreviewStatusListener.PreviewAreaSizeChangedListener listener) { 878 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener); 879 } 880 881 /** 882 * This inflates generic_module layout, which contains all the shared views across 883 * modules. Then each module inflates their own views in the given view group. For 884 * now, this is called every time switching from a not-yet-refactored module to a 885 * refactored module. In the future, this should only need to be done once per app 886 * start. 887 */ 888 public void prepareModuleUI() { 889 mCameraRootView.removeAllViews(); 890 LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext() 891 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 892 inflater.inflate(R.layout.generic_module, mCameraRootView, true); 893 894 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout); 895 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content); 896 mTextureViewHelper = new TextureViewHelper(mTextureView); 897 mTextureViewHelper.setSurfaceTextureListener(this); 898 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener); 899 900 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar); 901 int unpressedColor = mController.getAndroidContext().getResources() 902 .getColor(R.color.bottombar_unpressed); 903 setBottomBarColor(unpressedColor); 904 int pressedColor = mController.getAndroidContext().getResources() 905 .getColor(R.color.bottombar_pressed); 906 setBottomBarPressedColor(pressedColor); 907 908 // Auto adjust the bottom bar for every module, so that panorama 909 // and photosphere can get a transparent bottom bar without any extra work. 910 // TODO: add and remove the bottom bar listener in onPreviewListenerChanged, 911 // based on whether the module needs to adjust the size of the bottom bar. 912 mTextureViewHelper.addPreviewAreaSizeChangedListener(mBottomBar); 913 914 mModeOptionsOverlay 915 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay); 916 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeOptionsOverlay); 917 918 // Sets the visibility of the bottom bar and the mode options. 919 resetBottomControls(mController.getCurrentModuleController(), 920 mController.getCurrentModuleIndex()); 921 922 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines); 923 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines); 924 925 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay); 926 mPreviewOverlay.setOnTouchListener(new MyTouchListener()); 927 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay); 928 929 mCaptureOverlay = (CaptureAnimationOverlay) 930 mCameraRootView.findViewById(R.id.capture_overlay); 931 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay); 932 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay); 933 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView); 934 935 if (mIndicatorIconController == null) { 936 mIndicatorIconController = 937 new IndicatorIconController(mController, mAppRootView); 938 } 939 mIndicatorIconController.setListener(mModeOptionsOverlay); 940 941 mController.getButtonManager().load(mCameraRootView); 942 mController.getButtonManager().setListener(mIndicatorIconController); 943 mController.getSettingsManager().addListener(mIndicatorIconController); 944 } 945 946 /** 947 * Called indirectly from each module in their initialization to get a view group 948 * to inflate the module specific views in. 949 * 950 * @return a view group for modules to attach views to 951 */ 952 public FrameLayout getModuleRootView() { 953 // TODO: Change it to mModuleUI when refactor is done 954 return mCameraRootView; 955 } 956 957 /** 958 * Remove all the module specific views. 959 */ 960 public void clearModuleUI() { 961 if (mModuleUI != null) { 962 mModuleUI.removeAllViews(); 963 } 964 mTextureViewHelper.addPreviewAreaSizeChangedListener(null); 965 966 mPreviewStatusListener = null; 967 mPreviewOverlay.reset(); 968 } 969 970 /** 971 * Gets called when preview is ready to start. It sets up one shot preview callback 972 * in order to receive a callback when the preview frame is available, so that 973 * the preview cover can be hidden to reveal preview. 974 * 975 * An alternative for getting the timing to hide preview cover is through 976 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)}, 977 * which is less accurate but therefore is the fallback for modules that manage 978 * their own preview callbacks (as setting one preview callback will override 979 * any other installed preview callbacks), or use camera2 API. 980 */ 981 public void onPreviewReadyToStart() { 982 if (mModeCoverState == COVER_SHOWN) { 983 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME; 984 mController.setupOneShotPreviewListener(); 985 } 986 } 987 988 /** 989 * Gets called when preview is started. 990 */ 991 public void onPreviewStarted() { 992 if (mModeCoverState == COVER_SHOWN) { 993 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE; 994 } 995 } 996 997 /** 998 * Gets notified when next preview frame comes in. 999 */ 1000 public void onNewPreviewFrame() { 1001 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1002 hideModeCover(); 1003 mModeCoverState = COVER_HIDDEN; 1004 } 1005 1006 /** 1007 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView} 1008 * 1009 * @param modeIndex mode index of the selected mode 1010 */ 1011 @Override 1012 public void onModeSelected(int modeIndex) { 1013 mHideCoverRunnable = new Runnable() { 1014 @Override 1015 public void run() { 1016 mModeListView.startModeSelectionAnimation(); 1017 } 1018 }; 1019 mModeCoverState = COVER_SHOWN; 1020 1021 int lastIndex = mController.getCurrentModuleIndex(); 1022 mController.onModeSelected(modeIndex); 1023 int currentIndex = mController.getCurrentModuleIndex(); 1024 1025 if (lastIndex == currentIndex) { 1026 hideModeCover(); 1027 } 1028 } 1029 1030 @Override 1031 public int getCurrentModeIndex() { 1032 return mController.getCurrentModuleIndex(); 1033 } 1034 1035 /********************** Capture animation **********************/ 1036 /* TODO: This session is subject to UX changes. In addition to the generic 1037 flash animation and post capture animation, consider designating a parameter 1038 for specifying the type of animation, as well as an animation finished listener 1039 so that modules can have more knowledge of the status of the animation. */ 1040 1041 /** 1042 * Starts the pre-capture animation. 1043 */ 1044 public void startPreCaptureAnimation() { 1045 mCaptureOverlay.startFlashAnimation(); 1046 } 1047 1048 /** 1049 * Cancels the pre-capture animation. 1050 */ 1051 public void cancelPreCaptureAnimation() { 1052 mAnimationManager.cancelAnimations(); 1053 } 1054 1055 /** 1056 * Cancels the post-capture animation. 1057 */ 1058 public void cancelPostCaptureAnimation() { 1059 mAnimationManager.cancelAnimations(); 1060 } 1061 1062 public FilmstripContentPanel getFilmstripContentPanel() { 1063 return mFilmstripPanel; 1064 } 1065 1066 /** 1067 * @return The {@link com.android.camera.app.CameraAppUI.BottomControls} on the 1068 * bottom of the filmstrip. 1069 */ 1070 public BottomControls getFilmstripBottomControls() { 1071 return mFilmstripBottomControls; 1072 } 1073 1074 /** 1075 * @param listener The listener for bottom controls. 1076 */ 1077 public void setFilmstripBottomControlsListener(BottomControls.Listener listener) { 1078 mFilmstripBottomControls.setListener(listener); 1079 } 1080 1081 /***************************SurfaceTexture Api and Listener*********************************/ 1082 1083 /** 1084 * Return the shared surface texture. 1085 */ 1086 public SurfaceTexture getSurfaceTexture() { 1087 return mSurface; 1088 } 1089 1090 /** 1091 * Return the shared {@link android.graphics.SurfaceTexture}'s width. 1092 */ 1093 public int getSurfaceWidth() { 1094 return mSurfaceWidth; 1095 } 1096 1097 /** 1098 * Return the shared {@link android.graphics.SurfaceTexture}'s height. 1099 */ 1100 public int getSurfaceHeight() { 1101 return mSurfaceHeight; 1102 } 1103 1104 @Override 1105 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 1106 mSurface = surface; 1107 mSurfaceWidth = width; 1108 mSurfaceHeight = height; 1109 Log.v(TAG, "SurfaceTexture is available"); 1110 if (mPreviewStatusListener != null) { 1111 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height); 1112 } 1113 } 1114 1115 @Override 1116 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 1117 mSurface = surface; 1118 mSurfaceWidth = width; 1119 mSurfaceHeight = height; 1120 if (mPreviewStatusListener != null) { 1121 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height); 1122 } 1123 } 1124 1125 @Override 1126 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 1127 mSurface = null; 1128 Log.v(TAG, "SurfaceTexture is destroyed"); 1129 if (mPreviewStatusListener != null) { 1130 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface); 1131 } 1132 return false; 1133 } 1134 1135 @Override 1136 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 1137 mSurface = surface; 1138 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) { 1139 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1140 hideModeCover(); 1141 mModeCoverState = COVER_HIDDEN; 1142 } 1143 if (mPreviewStatusListener != null) { 1144 mPreviewStatusListener.onSurfaceTextureUpdated(surface); 1145 } 1146 } 1147 1148 /****************************Grid lines api ******************************/ 1149 1150 /** 1151 * Show a set of evenly spaced lines over the preview. The number 1152 * of lines horizontally and vertically is determined by 1153 * {@link com.android.camera.ui.GridLines}. 1154 */ 1155 public void showGridLines() { 1156 if (mGridLines != null) { 1157 mGridLines.setVisibility(View.VISIBLE); 1158 } 1159 } 1160 1161 /** 1162 * Hide the set of evenly spaced grid lines overlaying the preview. 1163 */ 1164 public void hideGridLines() { 1165 if (mGridLines != null) { 1166 mGridLines.setVisibility(View.INVISIBLE); 1167 } 1168 } 1169 1170 /** 1171 * Return a callback which shows or hide the preview grid lines 1172 * depending on whether the grid lines setting is set on. 1173 */ 1174 public ButtonManager.ButtonCallback getGridLinesCallback() { 1175 return new ButtonManager.ButtonCallback() { 1176 @Override 1177 public void onStateChanged(int state) { 1178 if (mController.getSettingsManager().areGridLinesOn()) { 1179 showGridLines(); 1180 } else { 1181 hideGridLines(); 1182 } 1183 } 1184 }; 1185 } 1186 1187 /***************************Mode options api *****************************/ 1188 1189 /** 1190 * Set the mode options visible. 1191 */ 1192 public void showModeOptions() { 1193 mModeOptionsOverlay.setVisibility(View.VISIBLE); 1194 } 1195 1196 /** 1197 * Set the mode options invisible. This is necessary for modes 1198 * that don't show a bottom bar for the capture UI. 1199 */ 1200 public void hideModeOptions() { 1201 mModeOptionsOverlay.setVisibility(View.INVISIBLE); 1202 } 1203 1204 /****************************Bottom bar api ******************************/ 1205 1206 /** 1207 * Sets up the bottom bar and mode options with the correct 1208 * shutter button and visibility based on the current module. 1209 */ 1210 public void resetBottomControls(ModuleController module, int moduleIndex) { 1211 if (areBottomControlsUsed(module)) { 1212 setBottomBarShutterIcon(moduleIndex); 1213 } 1214 } 1215 1216 /** 1217 * Show or hide the mode options and bottom bar, based on 1218 * whether the current module is using the bottom bar. Returns 1219 * whether the mode options and bottom bar are used. 1220 */ 1221 private boolean areBottomControlsUsed(ModuleController module) { 1222 if (module.isUsingBottomBar()) { 1223 showBottomBar(); 1224 showModeOptions(); 1225 return true; 1226 } else { 1227 hideBottomBar(); 1228 hideModeOptions(); 1229 return false; 1230 } 1231 } 1232 1233 /** 1234 * Set the bottom bar visible. 1235 */ 1236 public void showBottomBar() { 1237 mBottomBar.setVisibility(View.VISIBLE); 1238 } 1239 1240 /** 1241 * Set the bottom bar invisible. 1242 */ 1243 public void hideBottomBar() { 1244 mBottomBar.setVisibility(View.INVISIBLE); 1245 } 1246 1247 /** 1248 * Sets the color of the bottom bar. 1249 */ 1250 public void setBottomBarColor(int colorId) { 1251 mBottomBar.setBackgroundColor(colorId); 1252 } 1253 1254 /** 1255 * Sets the pressed color of the bottom bar. 1256 */ 1257 public void setBottomBarPressedColor(int colorId) { 1258 mBottomBar.setBackgroundPressedColor(colorId); 1259 } 1260 1261 /** 1262 * Sets the shutter button icon on the bottom bar, based on 1263 * the mode index. 1264 */ 1265 public void setBottomBarShutterIcon(int modeIndex) { 1266 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex, 1267 mController.getAndroidContext()); 1268 mBottomBar.setShutterButtonIcon(shutterIconId); 1269 } 1270 1271 public void animateBottomBarToCircle(int shutterIconId) { 1272 mBottomBar.animateToCircle(shutterIconId); 1273 } 1274 1275 public void animateBottomBarToFullSize(int shutterIconId) { 1276 mBottomBar.animateToFullSize(shutterIconId); 1277 } 1278 1279 /** 1280 * Set the visibility of the bottom bar. 1281 */ 1282 // TODO: needed for when panorama is managed by the generic module ui. 1283 public void setBottomBarVisible(boolean visible) { 1284 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1285 } 1286 1287 /** 1288 * If the bottom bar is visible (hence has been drawn), 1289 * this sets a {@link #ShutterButton.OnShutterButtonListener} 1290 * on the global shutter button, 1291 */ 1292 public void setBottomBarShutterListener( 1293 ShutterButton.OnShutterButtonListener listener) { 1294 ShutterButton shutterButton 1295 = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button); 1296 if (shutterButton != null) { 1297 shutterButton.setOnShutterButtonListener(listener); 1298 } 1299 } 1300 1301 /** 1302 * Performs a transition to the capture layout of the bottom bar. 1303 */ 1304 public void transitionToCapture() { 1305 ModuleController moduleController = mController.getCurrentModuleController(); 1306 applyModuleSpecs(moduleController.getHardwareSpec(), 1307 moduleController.getBottomBarSpec()); 1308 mBottomBar.transitionToCapture(); 1309 } 1310 1311 /** 1312 * Performs a transition to the global intent layout. 1313 */ 1314 public void transitionToIntentCaptureLayout() { 1315 ModuleController moduleController = mController.getCurrentModuleController(); 1316 applyModuleSpecs(moduleController.getHardwareSpec(), 1317 moduleController.getBottomBarSpec()); 1318 mBottomBar.transitionToIntentCaptureLayout(); 1319 } 1320 1321 /** 1322 * Performs a transition to the global intent review layout. 1323 */ 1324 public void transitionToIntentReviewLayout() { 1325 ModuleController moduleController = mController.getCurrentModuleController(); 1326 applyModuleSpecs(moduleController.getHardwareSpec(), 1327 moduleController.getBottomBarSpec()); 1328 mBottomBar.transitionToIntentReviewLayout(); 1329 } 1330 1331 /** 1332 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec} 1333 * to the bottom bar mode options based on limitations from a 1334 * {@link com.android.camera.hardware.HardwareSpec}. 1335 * 1336 * Options not supported by the hardware are either hidden 1337 * or disabled, depending on the option. 1338 * 1339 * Otherwise, the option is fully enabled and clickable. 1340 */ 1341 public void applyModuleSpecs(final HardwareSpec hardwareSpec, 1342 final BottomBarUISpec bottomBarSpec) { 1343 if (hardwareSpec == null || bottomBarSpec == null) { 1344 return; 1345 } 1346 1347 ButtonManager buttonManager = mController.getButtonManager(); 1348 SettingsManager settingsManager = mController.getSettingsManager(); 1349 1350 /** Standard mode options */ 1351 if (hardwareSpec.isFrontCameraSupported()) { 1352 if (bottomBarSpec.enableCamera) { 1353 buttonManager.enableButton(ButtonManager.BUTTON_CAMERA, 1354 bottomBarSpec.cameraCallback); 1355 } else { 1356 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA); 1357 } 1358 } else { 1359 // Hide camera icon if front camera not available. 1360 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA); 1361 } 1362 1363 if (bottomBarSpec.hideFlash) { 1364 buttonManager.hideButton(ButtonManager.BUTTON_FLASH); 1365 } else { 1366 if (hardwareSpec.isFlashSupported()) { 1367 if (bottomBarSpec.enableFlash && settingsManager.isCameraBackFacing()) { 1368 buttonManager.enableButton(ButtonManager.BUTTON_FLASH, bottomBarSpec.flashCallback); 1369 } else if (bottomBarSpec.enableTorchFlash && settingsManager.isCameraBackFacing()) { 1370 buttonManager.enableButton(ButtonManager.BUTTON_TORCH, bottomBarSpec.flashCallback); 1371 } else { 1372 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1373 } 1374 } else { 1375 // Disable flash icon if not supported by the hardware. 1376 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1377 } 1378 } 1379 1380 if (bottomBarSpec.hideHdr) { 1381 // Force hide hdr or hdr plus icon. 1382 buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS); 1383 } else { 1384 if (hardwareSpec.isHdrPlusSupported()) { 1385 if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) { 1386 buttonManager.enableButton(ButtonManager.BUTTON_HDRPLUS, 1387 bottomBarSpec.hdrCallback); 1388 } else { 1389 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS); 1390 } 1391 } else if (hardwareSpec.isHdrSupported()) { 1392 if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) { 1393 buttonManager.enableButton(ButtonManager.BUTTON_HDR, 1394 bottomBarSpec.hdrCallback); 1395 } else { 1396 buttonManager.disableButton(ButtonManager.BUTTON_HDR); 1397 } 1398 } else { 1399 // Hide hdr plus or hdr icon if neither are supported. 1400 buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS); 1401 } 1402 } 1403 1404 if (bottomBarSpec.hideGridLines) { 1405 // Force hide grid lines icon. 1406 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES); 1407 hideGridLines(); 1408 } else { 1409 if (bottomBarSpec.enableGridLines) { 1410 buttonManager.enableButton(ButtonManager.BUTTON_GRID_LINES, 1411 bottomBarSpec.gridLinesCallback != null ? 1412 bottomBarSpec.gridLinesCallback : getGridLinesCallback()); 1413 } else { 1414 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES); 1415 hideGridLines(); 1416 } 1417 } 1418 1419 if (bottomBarSpec.enablePanoHorizontal 1420 && PhotoSphereHelper.getPanoramaHorizontalDrawableId() > 0) { 1421 buttonManager.enablePushButton(ButtonManager.BUTTON_PANO_HORIZONTAL, 1422 bottomBarSpec.panoHorizontalCallback, 1423 PhotoSphereHelper.getPanoramaHorizontalDrawableId()); 1424 } 1425 1426 if (bottomBarSpec.enablePanoVertical 1427 && PhotoSphereHelper.getPanoramaVerticalDrawableId() > 0) { 1428 buttonManager.enablePushButton(ButtonManager.BUTTON_PANO_VERTICAL, 1429 bottomBarSpec.panoVerticalCallback, 1430 PhotoSphereHelper.getPanoramaVerticalDrawableId()); 1431 } 1432 1433 /** Intent UI */ 1434 if (bottomBarSpec.showCancel) { 1435 buttonManager.enablePushButton(ButtonManager.BUTTON_CANCEL, 1436 bottomBarSpec.cancelCallback); 1437 } 1438 if (bottomBarSpec.showDone) { 1439 buttonManager.enablePushButton(ButtonManager.BUTTON_DONE, 1440 bottomBarSpec.doneCallback); 1441 } 1442 if (bottomBarSpec.showRetake) { 1443 buttonManager.enablePushButton(ButtonManager.BUTTON_RETAKE, 1444 bottomBarSpec.retakeCallback); 1445 } 1446 } 1447} 1448