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