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