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