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