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