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