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