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