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