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