CameraAppUI.java revision 556a174d0f7cbd49cd8aaec6a5359440b42352c6
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 or not timer should show. 456 */ 457 public boolean enableSelfTimer = false; 458 } 459 460 461 private final static Log.Tag TAG = new Log.Tag("CameraAppUI"); 462 463 private final AppController mController; 464 private final boolean mIsCaptureIntent; 465 private final AnimationManager mAnimationManager; 466 467 // Swipe states: 468 private final static int IDLE = 0; 469 private final static int SWIPE_UP = 1; 470 private final static int SWIPE_DOWN = 2; 471 private final static int SWIPE_LEFT = 3; 472 private final static int SWIPE_RIGHT = 4; 473 private boolean mSwipeEnabled = true; 474 475 // Shared Surface Texture properities. 476 private SurfaceTexture mSurface; 477 private int mSurfaceWidth; 478 private int mSurfaceHeight; 479 480 // Touch related measures: 481 private final int mSlop; 482 private final static int SWIPE_TIME_OUT_MS = 500; 483 484 // Mode cover states: 485 private final static int COVER_HIDDEN = 0; 486 private final static int COVER_SHOWN = 1; 487 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2; 488 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3; 489 490 // App level views: 491 private final FrameLayout mCameraRootView; 492 private final ModeTransitionView mModeTransitionView; 493 private final MainActivityLayout mAppRootView; 494 private final ModeListView mModeListView; 495 private final FilmstripLayout mFilmstripLayout; 496 private TextureView mTextureView; 497 private FrameLayout mModuleUI; 498 private ShutterButton mShutterButton; 499 private BottomBar mBottomBar; 500 private ModeOptionsOverlay mModeOptionsOverlay; 501 private IndicatorIconController mIndicatorIconController; 502 private View mFocusOverlay; 503 private FrameLayout mTutorialsPlaceHolderWrapper; 504 private BottomBarModeOptionsWrapper mIndicatorBottomBarWrapper; 505 private TextureViewHelper mTextureViewHelper; 506 private final GestureDetector mGestureDetector; 507 private DisplayManager.DisplayListener mDisplayListener; 508 private int mLastRotation; 509 private int mSwipeState = IDLE; 510 private PreviewOverlay mPreviewOverlay; 511 private GridLines mGridLines; 512 private CaptureAnimationOverlay mCaptureOverlay; 513 private PreviewStatusListener mPreviewStatusListener; 514 private int mModeCoverState = COVER_HIDDEN; 515 private final FilmstripBottomPanel mFilmstripBottomControls; 516 private final FilmstripContentPanel mFilmstripPanel; 517 private Runnable mHideCoverRunnable; 518 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener 519 = new View.OnLayoutChangeListener() { 520 @Override 521 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 522 int oldTop, int oldRight, int oldBottom) { 523 if (mPreviewStatusListener != null) { 524 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft, 525 oldTop, oldRight, oldBottom); 526 } 527 } 528 }; 529 private View mModeOptionsToggle; 530 private final PeekView mPeekView; 531 private final CaptureLayoutHelper mCaptureLayoutHelper; 532 private boolean mAccessibilityEnabled; 533 private final View mAccessibilityAffordances; 534 535 /** 536 * Provides current preview frame and the controls/overlay from the module that 537 * are shown on top of the preview. 538 */ 539 public interface CameraModuleScreenShotProvider { 540 /** 541 * Returns the current preview frame down-sampled using the given down-sample 542 * factor. 543 * 544 * @param downSampleFactor the down sample factor for down sampling the 545 * preview frame. (e.g. a down sample factor of 546 * 2 means to scale down the preview frame to 1/2 547 * the width and height.) 548 * @return down-sampled preview frame 549 */ 550 public Bitmap getPreviewFrame(int downSampleFactor); 551 552 /** 553 * @return the controls and overlays that are currently showing on top of 554 * the preview drawn into a bitmap with no scaling applied. 555 */ 556 public Bitmap getPreviewOverlayAndControls(); 557 } 558 559 /** 560 * This listener gets called when the size of the window (excluding the system 561 * decor such as status bar and nav bar) has changed. 562 */ 563 public interface NonDecorWindowSizeChangedListener { 564 public void onNonDecorWindowSizeChanged(int width, int height, int rotation); 565 } 566 567 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider = 568 new CameraModuleScreenShotProvider() { 569 @Override 570 public Bitmap getPreviewFrame(int downSampleFactor) { 571 if (mCameraRootView == null || mTextureView == null) { 572 return null; 573 } 574 RectF previewArea = mTextureViewHelper.getPreviewArea(); 575 // Gets the bitmap from the preview TextureView. 576 Bitmap preview = mTextureView.getBitmap( 577 (int) previewArea.width() / downSampleFactor, 578 (int) previewArea.height() / downSampleFactor); 579 return preview; 580 } 581 582 @Override 583 public Bitmap getPreviewOverlayAndControls() { 584 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(), 585 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888); 586 Canvas canvas = new Canvas(overlays); 587 mCameraRootView.draw(canvas); 588 return overlays; 589 } 590 }; 591 592 private long mCoverHiddenTime = -1; // System time when preview cover was hidden. 593 594 public long getCoverHiddenTime() { 595 return mCoverHiddenTime; 596 } 597 598 /** 599 * This resets the preview to have no applied transform matrix. 600 */ 601 public void clearPreviewTransform() { 602 mTextureViewHelper.clearTransform(); 603 } 604 605 public void updatePreviewAspectRatio(float aspectRatio) { 606 mTextureViewHelper.updateAspectRatio(aspectRatio); 607 } 608 609 /** 610 * This is to support modules that calculate their own transform matrix because 611 * they need to use a transform matrix to rotate the preview. 612 * 613 * @param matrix transform matrix to be set on the TextureView 614 */ 615 public void updatePreviewTransform(Matrix matrix) { 616 mTextureViewHelper.updateTransform(matrix); 617 } 618 619 public interface AnimationFinishedListener { 620 public void onAnimationFinished(boolean success); 621 } 622 623 private class MyTouchListener implements View.OnTouchListener { 624 private boolean mScaleStarted = false; 625 @Override 626 public boolean onTouch(View v, MotionEvent event) { 627 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 628 mScaleStarted = false; 629 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { 630 mScaleStarted = true; 631 } 632 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event); 633 } 634 } 635 636 /** 637 * This gesture listener finds out the direction of the scroll gestures and 638 * sends them to CameraAppUI to do further handling. 639 */ 640 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener { 641 private MotionEvent mDown; 642 643 @Override 644 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) { 645 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS 646 || mSwipeState != IDLE 647 || mIsCaptureIntent 648 || !mSwipeEnabled) { 649 return false; 650 } 651 652 int deltaX = (int) (ev.getX() - mDown.getX()); 653 int deltaY = (int) (ev.getY() - mDown.getY()); 654 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { 655 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) { 656 // Calculate the direction of the swipe. 657 if (deltaX >= Math.abs(deltaY)) { 658 // Swipe right. 659 setSwipeState(SWIPE_RIGHT); 660 } else if (deltaX <= -Math.abs(deltaY)) { 661 // Swipe left. 662 setSwipeState(SWIPE_LEFT); 663 } 664 } 665 } 666 return true; 667 } 668 669 private void setSwipeState(int swipeState) { 670 mSwipeState = swipeState; 671 // Notify new swipe detected. 672 onSwipeDetected(swipeState); 673 } 674 675 @Override 676 public boolean onDown(MotionEvent ev) { 677 mDown = MotionEvent.obtain(ev); 678 mSwipeState = IDLE; 679 return false; 680 } 681 } 682 683 public CameraAppUI(AppController controller, MainActivityLayout appRootView, 684 boolean isCaptureIntent) { 685 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop(); 686 mController = controller; 687 mIsCaptureIntent = isCaptureIntent; 688 689 mAppRootView = appRootView; 690 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout); 691 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root); 692 mModeTransitionView = (ModeTransitionView) 693 mAppRootView.findViewById(R.id.mode_transition_view); 694 mFilmstripBottomControls = new FilmstripBottomPanel(controller, 695 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel)); 696 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout); 697 mGestureDetector = new GestureDetector(controller.getAndroidContext(), 698 new MyGestureListener()); 699 Resources res = controller.getAndroidContext().getResources(); 700 mCaptureLayoutHelper = new CaptureLayoutHelper( 701 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min), 702 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max), 703 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal)); 704 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout); 705 if (mModeListView != null) { 706 mModeListView.setModeSwitchListener(this); 707 mModeListView.setModeListOpenListener(this); 708 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider); 709 mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper); 710 } else { 711 Log.e(TAG, "Cannot find mode list in the view hierarchy"); 712 } 713 mAnimationManager = new AnimationManager(); 714 mPeekView = (PeekView) appRootView.findViewById(R.id.peek_view); 715 mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper); 716 initDisplayListener(); 717 mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances); 718 View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button); 719 modeListToggle.setOnClickListener(new View.OnClickListener() { 720 @Override 721 public void onClick(View view) { 722 openModeList(); 723 } 724 }); 725 View filmstripToggle = mAppRootView.findViewById( 726 R.id.accessibility_filmstrip_toggle_button); 727 filmstripToggle.setOnClickListener(new View.OnClickListener() { 728 @Override 729 public void onClick(View view) { 730 showFilmstrip(); 731 } 732 }); 733 } 734 735 /** 736 * Creates a cling for the specific viewer and links the cling to the corresponding 737 * button for layout position. 738 * 739 * @param viewerType defines which viewer the cling is for. 740 */ 741 public void setupClingForViewer(int viewerType) { 742 if (viewerType == BottomPanel.VIEWER_REFOCUS) { 743 FrameLayout filmstripContent = (FrameLayout) mAppRootView 744 .findViewById(R.id.camera_filmstrip_content_layout); 745 if (filmstripContent != null) { 746 // Creates refocus cling. 747 LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext() 748 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 749 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false); 750 // Sets instruction text in the cling. 751 refocusCling.setText(mController.getAndroidContext().getResources() 752 .getString(R.string.cling_text_for_refocus_editor_button)); 753 754 // Adds cling into view hierarchy. 755 int clingWidth = mController.getAndroidContext() 756 .getResources().getDimensionPixelSize(R.dimen.default_cling_width); 757 filmstripContent.addView(refocusCling, clingWidth, 758 ViewGroup.LayoutParams.WRAP_CONTENT); 759 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling); 760 } 761 } 762 } 763 764 /** 765 * Clears the listeners for the cling and remove it from the view hierarchy. 766 * 767 * @param viewerType defines which viewer the cling is for. 768 */ 769 public void clearClingForViewer(int viewerType) { 770 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType); 771 if (clingToBeRemoved == null) { 772 // No cling is created for the specific viewer type. 773 return; 774 } 775 mFilmstripBottomControls.clearClingForViewer(viewerType); 776 clingToBeRemoved.setVisibility(View.GONE); 777 mAppRootView.removeView(clingToBeRemoved); 778 } 779 780 /** 781 * Enable or disable swipe gestures. We want to disable them e.g. while we 782 * record a video. 783 */ 784 public void setSwipeEnabled(boolean enabled) { 785 mSwipeEnabled = enabled; 786 // TODO: This can be removed once we come up with a new design for handling swipe 787 // on shutter button and mode options. (More details: b/13751653) 788 mAppRootView.setSwipeEnabled(enabled); 789 } 790 791 public void onDestroy() { 792 ((DisplayManager) mController.getAndroidContext() 793 .getSystemService(Context.DISPLAY_SERVICE)) 794 .unregisterDisplayListener(mDisplayListener); 795 } 796 797 /** 798 * Initializes the display listener to listen to display changes such as 799 * 180-degree rotation change, which will not have an onConfigurationChanged 800 * callback. 801 */ 802 private void initDisplayListener() { 803 if (ApiHelper.HAS_DISPLAY_LISTENER) { 804 mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext()); 805 806 mDisplayListener = new DisplayManager.DisplayListener() { 807 @Override 808 public void onDisplayAdded(int arg0) { 809 // Do nothing. 810 } 811 812 @Override 813 public void onDisplayChanged(int displayId) { 814 int rotation = CameraUtil.getDisplayRotation( 815 mController.getAndroidContext()); 816 if ((rotation - mLastRotation + 360) % 360 == 180 817 && mPreviewStatusListener != null) { 818 mPreviewStatusListener.onPreviewFlipped(); 819 mIndicatorBottomBarWrapper.requestLayout(); 820 mModeListView.requestLayout(); 821 mTextureView.requestLayout(); 822 } 823 mLastRotation = rotation; 824 } 825 826 @Override 827 public void onDisplayRemoved(int arg0) { 828 // Do nothing. 829 } 830 }; 831 832 ((DisplayManager) mController.getAndroidContext() 833 .getSystemService(Context.DISPLAY_SERVICE)) 834 .registerDisplayListener(mDisplayListener, null); 835 } 836 } 837 838 /** 839 * Redirects touch events to appropriate recipient views based on swipe direction. 840 * More specifically, swipe up and swipe down will be handled by the view that handles 841 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected 842 * to mode list in order to bring up mode list. 843 */ 844 private void onSwipeDetected(int swipeState) { 845 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) { 846 // TODO: Polish quick switch after this release. 847 // Quick switch between modes. 848 int currentModuleIndex = mController.getCurrentModuleIndex(); 849 final int moduleToTransitionTo = 850 mController.getQuickSwitchToModuleId(currentModuleIndex); 851 if (currentModuleIndex != moduleToTransitionTo) { 852 mAppRootView.redirectTouchEventsTo(mModeTransitionView); 853 854 int shadeColorId = R.color.mode_cover_default_color; 855 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo, 856 mController.getAndroidContext()); 857 858 AnimationFinishedListener listener = new AnimationFinishedListener() { 859 @Override 860 public void onAnimationFinished(boolean success) { 861 if (success) { 862 mHideCoverRunnable = new Runnable() { 863 @Override 864 public void run() { 865 mModeTransitionView.startPeepHoleAnimation(); 866 } 867 }; 868 mModeCoverState = COVER_SHOWN; 869 // Go to new module when the previous operation is successful. 870 mController.onModeSelected(moduleToTransitionTo); 871 } 872 } 873 }; 874 if (mSwipeState == SWIPE_UP) { 875 mModeTransitionView.prepareToPullUpShade(shadeColorId, iconRes, listener); 876 } else { 877 mModeTransitionView.prepareToPullDownShade(shadeColorId, iconRes, listener); 878 } 879 } 880 } else if (swipeState == SWIPE_LEFT) { 881 // Pass the touch sequence to filmstrip layout. 882 mAppRootView.redirectTouchEventsTo(mFilmstripLayout); 883 } else if (swipeState == SWIPE_RIGHT) { 884 // Pass the touch to mode switcher 885 mAppRootView.redirectTouchEventsTo(mModeListView); 886 } 887 } 888 889 /** 890 * Gets called when activity resumes in preview. 891 */ 892 public void resume() { 893 // Show mode theme cover until preview is ready 894 showModeCoverUntilPreviewReady(); 895 896 // Hide action bar first since we are in full screen mode first, and 897 // switch the system UI to lights-out mode. 898 mFilmstripPanel.hide(); 899 900 // Show UI that is meant to only be used when spoken feedback is 901 // enabled. 902 mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled(); 903 mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE); 904 } 905 906 /** 907 * @return Whether any spoken feedback accessibility feature is currently 908 * enabled. 909 */ 910 private boolean isSpokenFeedbackAccessibilityEnabled() { 911 AccessibilityManager accessibilityManager = (AccessibilityManager) mController 912 .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE); 913 List<AccessibilityServiceInfo> infos = accessibilityManager 914 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN); 915 return infos != null && !infos.isEmpty(); 916 } 917 918 /** 919 * Opens the mode list (e.g. because of the menu button being pressed) and 920 * adapts the rest of the UI. 921 */ 922 public void openModeList() { 923 mModeOptionsOverlay.closeModeOptions(); 924 mModeListView.onMenuPressed(); 925 } 926 927 /** 928 * A cover view showing the mode theme color and mode icon will be visible on 929 * top of preview until preview is ready (i.e. camera preview is started and 930 * the first frame has been received). 931 */ 932 private void showModeCoverUntilPreviewReady() { 933 int modeId = mController.getCurrentModuleIndex(); 934 int colorId = R.color.mode_cover_default_color;; 935 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext()); 936 mModeTransitionView.setupModeCover(colorId, iconId); 937 mHideCoverRunnable = new Runnable() { 938 @Override 939 public void run() { 940 mModeTransitionView.hideModeCover(null); 941 showShimmyDelayed(); 942 } 943 }; 944 mModeCoverState = COVER_SHOWN; 945 } 946 947 private void showShimmyDelayed() { 948 if (!mIsCaptureIntent) { 949 // Show shimmy in SHIMMY_DELAY_MS 950 mModeListView.showModeSwitcherHint(); 951 } 952 } 953 954 private void hideModeCover() { 955 if (mHideCoverRunnable != null) { 956 mAppRootView.post(mHideCoverRunnable); 957 mHideCoverRunnable = null; 958 } 959 mModeCoverState = COVER_HIDDEN; 960 if (mCoverHiddenTime < 0) { 961 mCoverHiddenTime = System.currentTimeMillis(); 962 } 963 } 964 965 966 public void onPreviewVisiblityChanged(int visibility) { 967 if (visibility == ModuleController.VISIBILITY_HIDDEN) { 968 setIndicatorBottomBarWrapperVisible(false); 969 mAccessibilityAffordances.setVisibility(View.GONE); 970 } else { 971 setIndicatorBottomBarWrapperVisible(true); 972 if (mAccessibilityEnabled) { 973 mAccessibilityAffordances.setVisibility(View.VISIBLE); 974 } else { 975 mAccessibilityAffordances.setVisibility(View.GONE); 976 } 977 } 978 } 979 980 /** 981 * Call to stop the preview from being rendered. 982 */ 983 public void pausePreviewRendering() { 984 mTextureView.setVisibility(View.INVISIBLE); 985 } 986 987 /** 988 * Call to begin rendering the preview again. 989 */ 990 public void resumePreviewRendering() { 991 mTextureView.setVisibility(View.VISIBLE); 992 } 993 994 @Override 995 public void onOpenFullScreen() { 996 // Do nothing. 997 } 998 999 @Override 1000 public void onModeListOpenProgress(float progress) { 1001 progress = 1 - progress; 1002 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress); 1003 mModeOptionsToggle.setAlpha(interpolatedProgress); 1004 // Change shutter button alpha linearly based on the mode list open progress: 1005 // set the alpha to disabled alpha when list is fully open, to enabled alpha 1006 // when the list is fully closed. 1007 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED 1008 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED); 1009 } 1010 1011 @Override 1012 public void onModeListClosed() { 1013 // Make sure the alpha on mode options ellipse is reset when mode drawer 1014 // is closed. 1015 mModeOptionsToggle.setAlpha(1f); 1016 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED); 1017 } 1018 1019 /** 1020 * Called when the back key is pressed. 1021 * 1022 * @return Whether the UI responded to the key event. 1023 */ 1024 public boolean onBackPressed() { 1025 if (mFilmstripLayout.getVisibility() == View.VISIBLE) { 1026 return mFilmstripLayout.onBackPressed(); 1027 } else { 1028 return mModeListView.onBackPressed(); 1029 } 1030 } 1031 1032 /** 1033 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that 1034 * listens to SurfaceTexture changes. In addition, listeners are set on 1035 * dependent app ui elements. 1036 * 1037 * @param previewStatusListener the listener that gets notified when SurfaceTexture 1038 * changes 1039 */ 1040 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) { 1041 mPreviewStatusListener = previewStatusListener; 1042 if (mPreviewStatusListener != null) { 1043 onPreviewListenerChanged(); 1044 } 1045 } 1046 1047 /** 1048 * When the PreviewStatusListener changes, listeners need to be 1049 * set on the following app ui elements: 1050 * {@link com.android.camera.ui.PreviewOverlay}, 1051 * {@link com.android.camera.ui.BottomBar}, 1052 * {@link com.android.camera.ui.IndicatorIconController}. 1053 */ 1054 private void onPreviewListenerChanged() { 1055 // Set a listener for recognizing preview gestures. 1056 GestureDetector.OnGestureListener gestureListener 1057 = mPreviewStatusListener.getGestureListener(); 1058 if (gestureListener != null) { 1059 mPreviewOverlay.setGestureListener(gestureListener); 1060 } 1061 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener(); 1062 if (touchListener != null) { 1063 mPreviewOverlay.setTouchListener(touchListener); 1064 } 1065 1066 mTextureViewHelper.setAutoAdjustTransform( 1067 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout()); 1068 } 1069 1070 /** 1071 * This method should be called in onCameraOpened. It defines CameraAppUI 1072 * specific changes that depend on the camera or camera settings. 1073 */ 1074 public void onChangeCamera() { 1075 ModuleController moduleController = mController.getCurrentModuleController(); 1076 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec()); 1077 1078 if (mIndicatorIconController != null) { 1079 // Sync the settings state with the indicator state. 1080 mIndicatorIconController.syncIndicators(); 1081 } 1082 } 1083 1084 /** 1085 * Adds a listener to receive callbacks when preview area changes. 1086 */ 1087 public void addPreviewAreaChangedListener( 1088 PreviewStatusListener.PreviewAreaChangedListener listener) { 1089 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener); 1090 } 1091 1092 /** 1093 * Removes a listener that receives callbacks when preview area changes. 1094 */ 1095 public void removePreviewAreaChangedListener( 1096 PreviewStatusListener.PreviewAreaChangedListener listener) { 1097 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener); 1098 } 1099 1100 /** 1101 * This inflates generic_module layout, which contains all the shared views across 1102 * modules. Then each module inflates their own views in the given view group. For 1103 * now, this is called every time switching from a not-yet-refactored module to a 1104 * refactored module. In the future, this should only need to be done once per app 1105 * start. 1106 */ 1107 public void prepareModuleUI() { 1108 mController.getSettingsManager().addListener(this); 1109 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout); 1110 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content); 1111 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper); 1112 mTextureViewHelper.setSurfaceTextureListener(this); 1113 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener); 1114 1115 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar); 1116 int unpressedColor = mController.getAndroidContext().getResources() 1117 .getColor(R.color.bottombar_unpressed); 1118 setBottomBarColor(unpressedColor); 1119 int pressedColor = mController.getAndroidContext().getResources() 1120 .getColor(R.color.bottombar_pressed); 1121 setBottomBarPressedColor(pressedColor); 1122 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper); 1123 1124 mModeOptionsOverlay 1125 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay); 1126 1127 // Sets the visibility of the bottom bar and the mode options. 1128 resetBottomControls(mController.getCurrentModuleController(), 1129 mController.getCurrentModuleIndex()); 1130 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper); 1131 1132 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button); 1133 addShutterListener(mController.getCurrentModuleController()); 1134 addShutterListener(mModeOptionsOverlay); 1135 addShutterListener(this); 1136 1137 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines); 1138 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines); 1139 1140 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay); 1141 mPreviewOverlay.setOnTouchListener(new MyTouchListener()); 1142 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay); 1143 1144 mCaptureOverlay = (CaptureAnimationOverlay) 1145 mCameraRootView.findViewById(R.id.capture_overlay); 1146 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay); 1147 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay); 1148 1149 if (mIndicatorIconController == null) { 1150 mIndicatorIconController = 1151 new IndicatorIconController(mController, mAppRootView); 1152 } 1153 1154 mController.getButtonManager().load(mCameraRootView); 1155 mController.getButtonManager().setListener(mIndicatorIconController); 1156 mController.getSettingsManager().addListener(mIndicatorIconController); 1157 1158 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle); 1159 mFocusOverlay = mCameraRootView.findViewById(R.id.focus_overlay); 1160 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView 1161 .findViewById(R.id.tutorials_placeholder_wrapper); 1162 mIndicatorBottomBarWrapper = (BottomBarModeOptionsWrapper) mAppRootView 1163 .findViewById(R.id.indicator_bottombar_wrapper); 1164 mIndicatorBottomBarWrapper.setCaptureLayoutHelper(mCaptureLayoutHelper); 1165 mTextureViewHelper.addPreviewAreaSizeChangedListener( 1166 new PreviewStatusListener.PreviewAreaChangedListener() { 1167 @Override 1168 public void onPreviewAreaChanged(RectF previewArea) { 1169 mPeekView.setTranslationX(previewArea.right - mAppRootView.getRight()); 1170 } 1171 }); 1172 1173 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView); 1174 mTextureViewHelper.addAspectRatioChangedListener( 1175 new PreviewStatusListener.PreviewAspectRatioChangedListener() { 1176 @Override 1177 public void onPreviewAspectRatioChanged(float aspectRatio) { 1178 mModeOptionsOverlay.requestLayout(); 1179 mBottomBar.requestLayout(); 1180 } 1181 } 1182 ); 1183 } 1184 1185 /** 1186 * Called indirectly from each module in their initialization to get a view group 1187 * to inflate the module specific views in. 1188 * 1189 * @return a view group for modules to attach views to 1190 */ 1191 public FrameLayout getModuleRootView() { 1192 // TODO: Change it to mModuleUI when refactor is done 1193 return mCameraRootView; 1194 } 1195 1196 /** 1197 * Remove all the module specific views. 1198 */ 1199 public void clearModuleUI() { 1200 if (mModuleUI != null) { 1201 mModuleUI.removeAllViews(); 1202 } 1203 removeShutterListener(mController.getCurrentModuleController()); 1204 mTutorialsPlaceHolderWrapper.removeAllViews(); 1205 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE); 1206 1207 setShutterButtonEnabled(true); 1208 mPreviewStatusListener = null; 1209 mPreviewOverlay.reset(); 1210 mFocusOverlay.setVisibility(View.INVISIBLE); 1211 } 1212 1213 /** 1214 * Gets called when preview is ready to start. It sets up one shot preview callback 1215 * in order to receive a callback when the preview frame is available, so that 1216 * the preview cover can be hidden to reveal preview. 1217 * 1218 * An alternative for getting the timing to hide preview cover is through 1219 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)}, 1220 * which is less accurate but therefore is the fallback for modules that manage 1221 * their own preview callbacks (as setting one preview callback will override 1222 * any other installed preview callbacks), or use camera2 API. 1223 */ 1224 public void onPreviewReadyToStart() { 1225 if (mModeCoverState == COVER_SHOWN) { 1226 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME; 1227 mController.setupOneShotPreviewListener(); 1228 } 1229 } 1230 1231 /** 1232 * Gets called when preview is started. 1233 */ 1234 public void onPreviewStarted() { 1235 if (mModeCoverState == COVER_SHOWN) { 1236 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE; 1237 } 1238 enableModeOptions(); 1239 } 1240 1241 /** 1242 * Gets notified when next preview frame comes in. 1243 */ 1244 public void onNewPreviewFrame() { 1245 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1246 hideModeCover(); 1247 mModeCoverState = COVER_HIDDEN; 1248 } 1249 1250 /** 1251 * Set the mode options toggle clickable. 1252 */ 1253 public void enableModeOptions() { 1254 /* 1255 * For modules using camera 1 api, this gets called in 1256 * onSurfaceTextureUpdated whenever the preview gets stopped and 1257 * started after each capture. This also takes care of the 1258 * case where the mode options might be unclickable when we 1259 * switch modes 1260 * 1261 * For modules using camera 2 api, they're required to call this 1262 * method when a capture is "completed". Unfortunately this differs 1263 * per module implementation. 1264 */ 1265 mModeOptionsOverlay.setToggleClickable(true); 1266 } 1267 1268 @Override 1269 public void onShutterButtonClick() { 1270 /* 1271 * Set the mode options toggle unclickable, generally 1272 * throughout the app, whenever the shutter button is clicked. 1273 * 1274 * This could be done in the OnShutterButtonListener of the 1275 * ModeOptionsOverlay, but since it is very important that we 1276 * can clearly see when the toggle becomes clickable again, 1277 * keep all of that logic at this level. 1278 */ 1279 mModeOptionsOverlay.setToggleClickable(false); 1280 } 1281 1282 @Override 1283 public void onShutterButtonFocus(boolean pressed) { 1284 // noop 1285 } 1286 1287 /** 1288 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView} 1289 * 1290 * @param modeIndex mode index of the selected mode 1291 */ 1292 @Override 1293 public void onModeSelected(int modeIndex) { 1294 mHideCoverRunnable = new Runnable() { 1295 @Override 1296 public void run() { 1297 mModeListView.startModeSelectionAnimation(); 1298 } 1299 }; 1300 mModeCoverState = COVER_SHOWN; 1301 1302 int lastIndex = mController.getCurrentModuleIndex(); 1303 mController.onModeSelected(modeIndex); 1304 int currentIndex = mController.getCurrentModuleIndex(); 1305 1306 if (lastIndex == currentIndex) { 1307 hideModeCover(); 1308 } 1309 } 1310 1311 @Override 1312 public void onSettingsSelected() { 1313 mController.onSettingsSelected(); 1314 } 1315 1316 @Override 1317 public int getCurrentModeIndex() { 1318 return mController.getCurrentModuleIndex(); 1319 } 1320 1321 /********************** Capture animation **********************/ 1322 /* TODO: This session is subject to UX changes. In addition to the generic 1323 flash animation and post capture animation, consider designating a parameter 1324 for specifying the type of animation, as well as an animation finished listener 1325 so that modules can have more knowledge of the status of the animation. */ 1326 1327 /** 1328 * Starts the filmstrip peek animation. 1329 * 1330 * @param bitmap The bitmap to show. 1331 * @param strong Whether the animation shows more portion of the bitmap or 1332 * not. 1333 */ 1334 public void startPeekAnimation(Bitmap bitmap, boolean strong) { 1335 if (mFilmstripLayout.getVisibility() == View.VISIBLE) { 1336 return; 1337 } 1338 mPeekView.startPeekAnimation(bitmap, strong); 1339 } 1340 1341 /** 1342 * Starts the pre-capture animation. 1343 */ 1344 public void startPreCaptureAnimation() { 1345 mCaptureOverlay.startFlashAnimation(); 1346 } 1347 1348 /** 1349 * Cancels the pre-capture animation. 1350 */ 1351 public void cancelPreCaptureAnimation() { 1352 mAnimationManager.cancelAnimations(); 1353 } 1354 1355 /** 1356 * Cancels the post-capture animation. 1357 */ 1358 public void cancelPostCaptureAnimation() { 1359 mAnimationManager.cancelAnimations(); 1360 } 1361 1362 public FilmstripContentPanel getFilmstripContentPanel() { 1363 return mFilmstripPanel; 1364 } 1365 1366 /** 1367 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the 1368 * bottom of the filmstrip. 1369 */ 1370 public BottomPanel getFilmstripBottomControls() { 1371 return mFilmstripBottomControls; 1372 } 1373 1374 /** 1375 * @param listener The listener for bottom controls. 1376 */ 1377 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) { 1378 mFilmstripBottomControls.setListener(listener); 1379 } 1380 1381 /***************************SurfaceTexture Api and Listener*********************************/ 1382 1383 /** 1384 * Return the shared surface texture. 1385 */ 1386 public SurfaceTexture getSurfaceTexture() { 1387 return mSurface; 1388 } 1389 1390 /** 1391 * Return the shared {@link android.graphics.SurfaceTexture}'s width. 1392 */ 1393 public int getSurfaceWidth() { 1394 return mSurfaceWidth; 1395 } 1396 1397 /** 1398 * Return the shared {@link android.graphics.SurfaceTexture}'s height. 1399 */ 1400 public int getSurfaceHeight() { 1401 return mSurfaceHeight; 1402 } 1403 1404 @Override 1405 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 1406 mSurface = surface; 1407 mSurfaceWidth = width; 1408 mSurfaceHeight = height; 1409 Log.v(TAG, "SurfaceTexture is available"); 1410 if (mPreviewStatusListener != null) { 1411 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height); 1412 } 1413 enableModeOptions(); 1414 } 1415 1416 @Override 1417 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 1418 mSurface = surface; 1419 mSurfaceWidth = width; 1420 mSurfaceHeight = height; 1421 if (mPreviewStatusListener != null) { 1422 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height); 1423 } 1424 } 1425 1426 @Override 1427 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 1428 mSurface = null; 1429 Log.v(TAG, "SurfaceTexture is destroyed"); 1430 if (mPreviewStatusListener != null) { 1431 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface); 1432 } 1433 return false; 1434 } 1435 1436 @Override 1437 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 1438 mSurface = surface; 1439 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) { 1440 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME); 1441 hideModeCover(); 1442 mModeCoverState = COVER_HIDDEN; 1443 } 1444 if (mPreviewStatusListener != null) { 1445 mPreviewStatusListener.onSurfaceTextureUpdated(surface); 1446 } 1447 } 1448 1449 /****************************Grid lines api ******************************/ 1450 1451 /** 1452 * Show a set of evenly spaced lines over the preview. The number 1453 * of lines horizontally and vertically is determined by 1454 * {@link com.android.camera.ui.GridLines}. 1455 */ 1456 public void showGridLines() { 1457 if (mGridLines != null) { 1458 mGridLines.setVisibility(View.VISIBLE); 1459 } 1460 } 1461 1462 /** 1463 * Hide the set of evenly spaced grid lines overlaying the preview. 1464 */ 1465 public void hideGridLines() { 1466 if (mGridLines != null) { 1467 mGridLines.setVisibility(View.INVISIBLE); 1468 } 1469 } 1470 1471 /** 1472 * Return a callback which shows or hide the preview grid lines 1473 * depending on whether the grid lines setting is set on. 1474 */ 1475 public ButtonManager.ButtonCallback getGridLinesCallback() { 1476 return new ButtonManager.ButtonCallback() { 1477 @Override 1478 public void onStateChanged(int state) { 1479 if (mController.getSettingsManager().areGridLinesOn()) { 1480 showGridLines(); 1481 } else { 1482 hideGridLines(); 1483 } 1484 } 1485 }; 1486 } 1487 1488 /***************************Mode options api *****************************/ 1489 1490 /** 1491 * Set the mode options visible. 1492 */ 1493 public void showModeOptions() { 1494 /* Make mode options clickable. */ 1495 enableModeOptions(); 1496 mModeOptionsOverlay.setVisibility(View.VISIBLE); 1497 } 1498 1499 /** 1500 * Set the mode options invisible. This is necessary for modes 1501 * that don't show a bottom bar for the capture UI. 1502 */ 1503 public void hideModeOptions() { 1504 mModeOptionsOverlay.setVisibility(View.INVISIBLE); 1505 } 1506 1507 /****************************Bottom bar api ******************************/ 1508 1509 /** 1510 * Sets up the bottom bar and mode options with the correct 1511 * shutter button and visibility based on the current module. 1512 */ 1513 public void resetBottomControls(ModuleController module, int moduleIndex) { 1514 if (areBottomControlsUsed(module)) { 1515 setBottomBarShutterIcon(moduleIndex); 1516 mCaptureLayoutHelper.setShowBottomBar(true); 1517 } else { 1518 mCaptureLayoutHelper.setShowBottomBar(false); 1519 } 1520 } 1521 1522 /** 1523 * Show or hide the mode options and bottom bar, based on 1524 * whether the current module is using the bottom bar. Returns 1525 * whether the mode options and bottom bar are used. 1526 */ 1527 private boolean areBottomControlsUsed(ModuleController module) { 1528 if (module.isUsingBottomBar()) { 1529 showBottomBar(); 1530 showModeOptions(); 1531 return true; 1532 } else { 1533 hideBottomBar(); 1534 hideModeOptions(); 1535 return false; 1536 } 1537 } 1538 1539 /** 1540 * Set the bottom bar visible. 1541 */ 1542 public void showBottomBar() { 1543 mBottomBar.setVisibility(View.VISIBLE); 1544 } 1545 1546 /** 1547 * Set the bottom bar invisible. 1548 */ 1549 public void hideBottomBar() { 1550 mBottomBar.setVisibility(View.INVISIBLE); 1551 } 1552 1553 /** 1554 * Sets the color of the bottom bar. 1555 */ 1556 public void setBottomBarColor(int colorId) { 1557 mBottomBar.setBackgroundColor(colorId); 1558 } 1559 1560 /** 1561 * Sets the pressed color of the bottom bar. 1562 */ 1563 public void setBottomBarPressedColor(int colorId) { 1564 mBottomBar.setBackgroundPressedColor(colorId); 1565 } 1566 1567 /** 1568 * Sets the shutter button icon on the bottom bar, based on 1569 * the mode index. 1570 */ 1571 public void setBottomBarShutterIcon(int modeIndex) { 1572 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex, 1573 mController.getAndroidContext()); 1574 mBottomBar.setShutterButtonIcon(shutterIconId); 1575 } 1576 1577 public void animateBottomBarToVideoStop(int shutterIconId) { 1578 mBottomBar.animateToVideoStop(shutterIconId); 1579 } 1580 1581 public void animateBottomBarToFullSize(int shutterIconId) { 1582 mBottomBar.animateToFullSize(shutterIconId); 1583 } 1584 1585 public void setShutterButtonEnabled(boolean enabled) { 1586 mBottomBar.setShutterButtonEnabled(enabled); 1587 } 1588 1589 public boolean isShutterButtonEnabled() { 1590 return mBottomBar.isShutterButtonEnabled(); 1591 } 1592 1593 public void setIndicatorBottomBarWrapperVisible(boolean visible) { 1594 mIndicatorBottomBarWrapper.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1595 } 1596 1597 /** 1598 * Set the visibility of the bottom bar. 1599 */ 1600 // TODO: needed for when panorama is managed by the generic module ui. 1601 public void setBottomBarVisible(boolean visible) { 1602 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 1603 } 1604 1605 /** 1606 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button. 1607 */ 1608 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) { 1609 mShutterButton.addOnShutterButtonListener(listener); 1610 } 1611 1612 /** 1613 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button. 1614 */ 1615 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) { 1616 mShutterButton.removeOnShutterButtonListener(listener); 1617 } 1618 1619 /** 1620 * Performs a transition to the capture layout of the bottom bar. 1621 */ 1622 public void transitionToCapture() { 1623 ModuleController moduleController = mController.getCurrentModuleController(); 1624 applyModuleSpecs(moduleController.getHardwareSpec(), 1625 moduleController.getBottomBarSpec()); 1626 mBottomBar.transitionToCapture(); 1627 } 1628 1629 /** 1630 * Displays the Cancel button instead of the capture button. 1631 */ 1632 public void transitionToCancel() { 1633 ModuleController moduleController = mController.getCurrentModuleController(); 1634 applyModuleSpecs(moduleController.getHardwareSpec(), 1635 moduleController.getBottomBarSpec()); 1636 mBottomBar.transitionToCancel(); 1637 } 1638 1639 /** 1640 * Performs a transition to the global intent layout. 1641 */ 1642 public void transitionToIntentCaptureLayout() { 1643 ModuleController moduleController = mController.getCurrentModuleController(); 1644 applyModuleSpecs(moduleController.getHardwareSpec(), 1645 moduleController.getBottomBarSpec()); 1646 mBottomBar.transitionToIntentCaptureLayout(); 1647 } 1648 1649 /** 1650 * Performs a transition to the global intent review layout. 1651 */ 1652 public void transitionToIntentReviewLayout() { 1653 ModuleController moduleController = mController.getCurrentModuleController(); 1654 applyModuleSpecs(moduleController.getHardwareSpec(), 1655 moduleController.getBottomBarSpec()); 1656 mBottomBar.transitionToIntentReviewLayout(); 1657 } 1658 1659 @Override 1660 public void onSettingChanged(SettingsManager settingsManager, int id) { 1661 // Update the mode options based on the hardware spec, 1662 // when hdr changes to prevent flash from getting out of sync. 1663 if (id == SettingsManager.SETTING_CAMERA_HDR) { 1664 ModuleController moduleController = mController.getCurrentModuleController(); 1665 applyModuleSpecs(moduleController.getHardwareSpec(), 1666 moduleController.getBottomBarSpec()); 1667 } 1668 } 1669 1670 /** 1671 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec} 1672 * to the bottom bar mode options based on limitations from a 1673 * {@link com.android.camera.hardware.HardwareSpec}. 1674 * 1675 * Options not supported by the hardware are either hidden 1676 * or disabled, depending on the option. 1677 * 1678 * Otherwise, the option is fully enabled and clickable. 1679 */ 1680 public void applyModuleSpecs(final HardwareSpec hardwareSpec, 1681 final BottomBarUISpec bottomBarSpec) { 1682 if (hardwareSpec == null || bottomBarSpec == null) { 1683 return; 1684 } 1685 1686 ButtonManager buttonManager = mController.getButtonManager(); 1687 SettingsManager settingsManager = mController.getSettingsManager(); 1688 1689 buttonManager.setToInitialState(); 1690 1691 /** Standard mode options */ 1692 if (hardwareSpec.isFrontCameraSupported()) { 1693 if (bottomBarSpec.enableCamera) { 1694 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA, 1695 bottomBarSpec.cameraCallback); 1696 } else { 1697 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA); 1698 } 1699 } else { 1700 // Hide camera icon if front camera not available. 1701 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA); 1702 } 1703 1704 boolean flashBackCamera = mController.getSettingsManager().getBoolean( 1705 SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA); 1706 if (bottomBarSpec.hideFlash || !flashBackCamera) { 1707 buttonManager.hideButton(ButtonManager.BUTTON_FLASH); 1708 } else { 1709 if (hardwareSpec.isFlashSupported()) { 1710 if (bottomBarSpec.enableFlash) { 1711 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH, 1712 bottomBarSpec.flashCallback); 1713 } else if (bottomBarSpec.enableTorchFlash) { 1714 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH, 1715 bottomBarSpec.flashCallback); 1716 } else if (bottomBarSpec.enableHdrPlusFlash) { 1717 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_TORCH, 1718 bottomBarSpec.flashCallback); 1719 } else { 1720 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1721 } 1722 } else { 1723 // Disable flash icon if not supported by the hardware. 1724 buttonManager.disableButton(ButtonManager.BUTTON_FLASH); 1725 } 1726 } 1727 1728 if (bottomBarSpec.hideHdr || mIsCaptureIntent) { 1729 // Force hide hdr or hdr plus icon. 1730 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS); 1731 } else { 1732 if (hardwareSpec.isHdrPlusSupported()) { 1733 if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) { 1734 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS, 1735 bottomBarSpec.hdrCallback); 1736 } else { 1737 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS); 1738 } 1739 } else if (hardwareSpec.isHdrSupported()) { 1740 if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) { 1741 buttonManager.initializeButton(ButtonManager.BUTTON_HDR, 1742 bottomBarSpec.hdrCallback); 1743 } else { 1744 buttonManager.disableButton(ButtonManager.BUTTON_HDR); 1745 } 1746 } else { 1747 // Hide hdr plus or hdr icon if neither are supported. 1748 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS); 1749 } 1750 } 1751 1752 if (bottomBarSpec.hideGridLines) { 1753 // Force hide grid lines icon. 1754 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES); 1755 hideGridLines(); 1756 } else { 1757 if (bottomBarSpec.enableGridLines) { 1758 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES, 1759 bottomBarSpec.gridLinesCallback != null ? 1760 bottomBarSpec.gridLinesCallback : getGridLinesCallback() 1761 ); 1762 } else { 1763 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES); 1764 hideGridLines(); 1765 } 1766 } 1767 1768 if (bottomBarSpec.enableSelfTimer) { 1769 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null); 1770 } else { 1771 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN); 1772 } 1773 1774 if (bottomBarSpec.enablePanoOrientation 1775 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) { 1776 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback); 1777 } 1778 1779 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation && 1780 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) && 1781 mController.getSettingsManager() 1782 .getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED); 1783 if (enableExposureCompensation) { 1784 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null); 1785 buttonManager.setExposureCompensationParameters( 1786 bottomBarSpec.minExposureCompensation, 1787 bottomBarSpec.maxExposureCompensation, 1788 bottomBarSpec.exposureCompensationStep); 1789 1790 buttonManager.setExposureCompensationCallback( 1791 bottomBarSpec.exposureCompensationSetCallback); 1792 buttonManager.updateExposureButtons(); 1793 } else { 1794 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION); 1795 buttonManager.setExposureCompensationCallback(null); 1796 } 1797 1798 /** Intent UI */ 1799 if (bottomBarSpec.showCancel) { 1800 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL, 1801 bottomBarSpec.cancelCallback); 1802 } 1803 if (bottomBarSpec.showDone) { 1804 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE, 1805 bottomBarSpec.doneCallback); 1806 } 1807 if (bottomBarSpec.showRetake) { 1808 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE, 1809 bottomBarSpec.retakeCallback); 1810 } 1811 if (bottomBarSpec.showReview) { 1812 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW, 1813 bottomBarSpec.reviewCallback, 1814 R.drawable.ic_play); 1815 } 1816 } 1817 1818 /** 1819 * Shows the given tutorial on the screen. 1820 */ 1821 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) { 1822 tutorial.show(mTutorialsPlaceHolderWrapper, inflater); 1823 } 1824 1825 /***************************Filmstrip api *****************************/ 1826 1827 public void showFilmstrip() { 1828 mModeListView.onBackPressed(); 1829 mFilmstripLayout.showFilmstrip(); 1830 } 1831 1832 public void hideFilmstrip() { 1833 mFilmstripLayout.hideFilmstrip(); 1834 } 1835} 1836