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