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