2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.camera.app;
19 import android.content.res.Resources;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.Matrix;
23 import android.graphics.RectF;
24 import android.graphics.SurfaceTexture;
25 import android.hardware.display.DisplayManager;
26 import android.util.CameraPerformanceTracker;
27 import android.view.GestureDetector;
28 import android.view.LayoutInflater;
29 import android.view.MotionEvent;
30 import android.view.TextureView;
31 import android.view.View;
32 import android.view.ViewConfiguration;
33 import android.view.ViewGroup;
34 import android.widget.FrameLayout;
35 import android.widget.ImageButton;
37 import com.android.camera.AccessibilityUtil;
38 import com.android.camera.AnimationManager;
39 import com.android.camera.ButtonManager;
40 import com.android.camera.CaptureLayoutHelper;
41 import com.android.camera.ShutterButton;
42 import com.android.camera.TextureViewHelper;
43 import com.android.camera.debug.Log;
44 import com.android.camera.filmstrip.FilmstripContentPanel;
45 import com.android.camera.hardware.HardwareSpec;
46 import com.android.camera.module.ModuleController;
47 import com.android.camera.settings.Keys;
48 import com.android.camera.settings.SettingsManager;
49 import com.android.camera.ui.AbstractTutorialOverlay;
50 import com.android.camera.ui.BottomBar;
51 import com.android.camera.ui.CaptureAnimationOverlay;
52 import com.android.camera.ui.GridLines;
53 import com.android.camera.ui.MainActivityLayout;
54 import com.android.camera.ui.ModeListView;
55 import com.android.camera.ui.ModeTransitionView;
56 import com.android.camera.ui.PreviewOverlay;
57 import com.android.camera.ui.PreviewStatusListener;
58 import com.android.camera.ui.StickyBottomCaptureLayout;
59 import com.android.camera.ui.TouchCoordinate;
60 import com.android.camera.ui.focus.FocusRing;
61 import com.android.camera.util.AndroidServices;
62 import com.android.camera.util.ApiHelper;
63 import com.android.camera.util.CameraUtil;
64 import com.android.camera.util.Gusterpolator;
65 import com.android.camera.util.PhotoSphereHelper;
66 import com.android.camera.widget.Cling;
67 import com.android.camera.widget.FilmstripLayout;
68 import com.android.camera.widget.IndicatorIconController;
69 import com.android.camera.widget.ModeOptionsOverlay;
70 import com.android.camera.widget.RoundedThumbnailView;
71 import com.android.camera2.R;
74 * CameraAppUI centralizes control of views shared across modules. Whereas module
75 * specific views will be handled in each Module UI. For example, we can now
76 * bring the flash animation and capture animation up from each module to app
77 * level, as these animations are largely the same for all modules.
79 * This class also serves to disambiguate touch events. It recognizes all the
80 * swipe gestures that happen on the preview by attaching a touch listener to
81 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
82 * of how swipe from each direction should be handled, it can then redirect these
83 * events to appropriate recipient views.
85 public class CameraAppUI implements ModeListView.ModeSwitchListener,
86 TextureView.SurfaceTextureListener,
87 ModeListView.ModeListOpenListener,
88 SettingsManager.OnSettingChangedListener,
89 ShutterButton.OnShutterButtonListener {
92 * The bottom controls on the filmstrip.
94 public static interface BottomPanel {
95 /** Values for the view state of the button. */
96 public final int VIEWER_NONE = 0;
97 public final int VIEWER_PHOTO_SPHERE = 1;
98 public final int VIEWER_REFOCUS = 2;
99 public final int VIEWER_OTHER = 3;
102 * Sets a new or replaces an existing listener for bottom control events.
104 void setListener(Listener listener);
107 * Sets cling for external viewer button.
109 void setClingForViewer(int viewerType, Cling cling);
112 * Clears cling for external viewer button.
114 void clearClingForViewer(int viewerType);
117 * Returns a cling for the specified viewer type.
119 Cling getClingForViewer(int viewerType);
122 * Set if the bottom controls are visible.
123 * @param visible {@code true} if visible.
125 void setVisible(boolean visible);
128 * @param visible Whether the button is visible.
130 void setEditButtonVisibility(boolean visible);
133 * @param enabled Whether the button is enabled.
135 void setEditEnabled(boolean enabled);
138 * Sets the visibility of the view-photosphere button.
140 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
141 * {@link #VIEWER_REFOCUS}.
143 void setViewerButtonVisibility(int state);
146 * @param enabled Whether the button is enabled.
148 void setViewEnabled(boolean enabled);
151 * @param enabled Whether the button is enabled.
153 void setTinyPlanetEnabled(boolean enabled);
156 * @param visible Whether the button is visible.
158 void setDeleteButtonVisibility(boolean visible);
161 * @param enabled Whether the button is enabled.
163 void setDeleteEnabled(boolean enabled);
166 * @param visible Whether the button is visible.
168 void setShareButtonVisibility(boolean visible);
171 * @param enabled Whether the button is enabled.
173 void setShareEnabled(boolean enabled);
176 * Sets the texts for progress UI.
178 * @param text The text to show.
180 void setProgressText(CharSequence text);
185 * @param progress The progress value. Should be between 0 and 100.
187 void setProgress(int progress);
190 * Replaces the progress UI with an error message.
192 void showProgressError(CharSequence message);
195 * Hide the progress error message.
197 void hideProgressError();
200 * Shows the progress.
205 * Hides the progress.
210 * Shows the controls.
215 * Hides the controls.
220 * Classes implementing this interface can listen for events on the bottom
223 public static interface Listener {
225 * Called when the user pressed the "view" button to e.g. view a photo
226 * sphere or RGBZ image.
228 public void onExternalViewer();
231 * Called when the "edit" button is pressed.
233 public void onEdit();
236 * Called when the "tiny planet" button is pressed.
238 public void onTinyPlanet();
241 * Called when the "delete" button is pressed.
243 public void onDelete();
246 * Called when the "share" button is pressed.
248 public void onShare();
251 * Called when the progress error message is clicked.
253 public void onProgressErrorClicked();
258 * BottomBarUISpec provides a structure for modules
259 * to specify their ideal bottom bar mode options layout.
261 * Once constructed by a module, this class should be
262 * treated as read only.
264 * The application then edits this spec according to
265 * hardware limitations and displays the final bottom
268 public static class BottomBarUISpec {
269 /** Mode options UI */
272 * Set true if the camera option should be enabled.
273 * If not set or false, and multiple cameras are supported,
274 * the camera option will be disabled.
276 * If multiple cameras are not supported, this preference
277 * is ignored and the camera option will not be visible.
279 public boolean enableCamera;
282 * Set true if the camera option should not be visible, regardless
283 * of hardware limitations.
285 public boolean hideCamera;
288 * Set true if the photo flash option should be enabled.
289 * If not set or false, the photo flash option will be
292 * If the hardware does not support multiple flash values,
293 * this preference is ignored and the flash option will
294 * be disabled. It will not be made invisible in order to
295 * preserve a consistent experience across devices and between
296 * front and back cameras.
298 public boolean enableFlash;
301 * Set true if the video flash option should be enabled.
302 * Same disable rules apply as the photo flash option.
304 public boolean enableTorchFlash;
307 * Set true if the HDR+ flash option should be enabled.
308 * Same disable rules apply as the photo flash option.
310 public boolean enableHdrPlusFlash;
313 * Set true if flash should not be visible, regardless of
314 * hardware limitations.
316 public boolean hideFlash;
319 * Set true if the hdr/hdr+ option should be enabled.
320 * If not set or false, the hdr/hdr+ option will be disabled.
322 * Hdr or hdr+ will be chosen based on hardware limitations,
323 * with hdr+ prefered.
325 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
326 * will not be visible.
328 public boolean enableHdr;
331 * Set true if hdr/hdr+ should not be visible, regardless of
332 * hardware limitations.
334 public boolean hideHdr;
337 * Set true if grid lines should be visible. Not setting this
338 * causes grid lines to be disabled. This option is agnostic to
341 public boolean enableGridLines;
344 * Set true if grid lines should not be visible.
346 public boolean hideGridLines;
349 * Set true if the panorama orientation option should be visible.
351 * This option is not constrained by hardware limitations.
353 public boolean enablePanoOrientation;
355 public boolean enableExposureCompensation;
360 * Set true if the intent ui cancel option should be visible.
362 public boolean showCancel;
364 * Set true if the intent ui done option should be visible.
366 public boolean showDone;
368 * Set true if the intent ui retake option should be visible.
370 public boolean showRetake;
372 * Set true if the intent ui review option should be visible.
374 public boolean showReview;
376 /** Mode options callbacks */
379 * A {@link com.android.camera.ButtonManager.ButtonCallback}
380 * that will be executed when the camera option is pressed. This
381 * callback can be null.
383 public ButtonManager.ButtonCallback cameraCallback;
386 * A {@link com.android.camera.ButtonManager.ButtonCallback}
387 * that will be executed when the flash option is pressed. This
388 * callback can be null.
390 public ButtonManager.ButtonCallback flashCallback;
393 * A {@link com.android.camera.ButtonManager.ButtonCallback}
394 * that will be executed when the hdr/hdr+ option is pressed. This
395 * callback can be null.
397 public ButtonManager.ButtonCallback hdrCallback;
400 * A {@link com.android.camera.ButtonManager.ButtonCallback}
401 * that will be executed when the grid lines option is pressed. This
402 * callback can be null.
404 public ButtonManager.ButtonCallback gridLinesCallback;
407 * A {@link com.android.camera.ButtonManager.ButtonCallback}
408 * that will execute when the panorama orientation option is pressed.
409 * This callback can be null.
411 public ButtonManager.ButtonCallback panoOrientationCallback;
413 /** Intent UI callbacks */
416 * A {@link android.view.View.OnClickListener} that will execute
417 * when the cancel option is pressed. This callback can be null.
419 public View.OnClickListener cancelCallback;
422 * A {@link android.view.View.OnClickListener} that will execute
423 * when the done option is pressed. This callback can be null.
425 public View.OnClickListener doneCallback;
428 * A {@link android.view.View.OnClickListener} that will execute
429 * when the retake option is pressed. This callback can be null.
431 public View.OnClickListener retakeCallback;
434 * A {@link android.view.View.OnClickListener} that will execute
435 * when the review option is pressed. This callback can be null.
437 public View.OnClickListener reviewCallback;
440 * A ExposureCompensationSetCallback that will execute
441 * when an expsosure button is pressed. This callback can be null.
443 public interface ExposureCompensationSetCallback {
444 public void setExposure(int value);
446 public ExposureCompensationSetCallback exposureCompensationSetCallback;
449 * Exposure compensation parameters.
451 public int minExposureCompensation;
452 public int maxExposureCompensation;
453 public float exposureCompensationStep;
456 * Whether self-timer is enabled.
458 public boolean enableSelfTimer = false;
461 * Whether the option for self-timer should show. If true and
462 * {@link #enableSelfTimer} is false, then the option should be shown
465 public boolean showSelfTimer = false;
469 private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
471 private final AppController mController;
472 private final boolean mIsCaptureIntent;
473 private final AnimationManager mAnimationManager;
476 private final static int IDLE = 0;
477 private final static int SWIPE_UP = 1;
478 private final static int SWIPE_DOWN = 2;
479 private final static int SWIPE_LEFT = 3;
480 private final static int SWIPE_RIGHT = 4;
481 private boolean mSwipeEnabled = true;
483 // Shared Surface Texture properities.
484 private SurfaceTexture mSurface;
485 private int mSurfaceWidth;
486 private int mSurfaceHeight;
488 // Touch related measures:
489 private final int mSlop;
490 private final static int SWIPE_TIME_OUT_MS = 500;
492 // Mode cover states:
493 private final static int COVER_HIDDEN = 0;
494 private final static int COVER_SHOWN = 1;
495 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
496 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
499 * Preview down-sample rate when taking a screenshot.
501 private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
504 private final FrameLayout mCameraRootView;
505 private final ModeTransitionView mModeTransitionView;
506 private final MainActivityLayout mAppRootView;
507 private final ModeListView mModeListView;
508 private final FilmstripLayout mFilmstripLayout;
509 private TextureView mTextureView;
510 private FrameLayout mModuleUI;
511 private ShutterButton mShutterButton;
512 private ImageButton mCountdownCancelButton;
513 private BottomBar mBottomBar;
514 private ModeOptionsOverlay mModeOptionsOverlay;
515 private IndicatorIconController mIndicatorIconController;
516 private FocusRing mFocusRing;
517 private FrameLayout mTutorialsPlaceHolderWrapper;
518 private StickyBottomCaptureLayout mStickyBottomCaptureLayout;
519 private TextureViewHelper mTextureViewHelper;
520 private final GestureDetector mGestureDetector;
521 private DisplayManager.DisplayListener mDisplayListener;
522 private int mLastRotation;
523 private int mSwipeState = IDLE;
524 private PreviewOverlay mPreviewOverlay;
525 private GridLines mGridLines;
526 private CaptureAnimationOverlay mCaptureOverlay;
527 private PreviewStatusListener mPreviewStatusListener;
528 private int mModeCoverState = COVER_HIDDEN;
529 private final FilmstripBottomPanel mFilmstripBottomControls;
530 private final FilmstripContentPanel mFilmstripPanel;
531 private Runnable mHideCoverRunnable;
532 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
533 = new View.OnLayoutChangeListener() {
535 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
536 int oldTop, int oldRight, int oldBottom) {
537 if (mPreviewStatusListener != null) {
538 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
539 oldTop, oldRight, oldBottom);
543 private View mModeOptionsToggle;
544 private final RoundedThumbnailView mRoundedThumbnailView;
545 private final CaptureLayoutHelper mCaptureLayoutHelper;
546 private final View mAccessibilityAffordances;
547 private AccessibilityUtil mAccessibilityUtil;
549 private boolean mDisableAllUserInteractions;
550 /** Whether to prevent capture indicator from being triggered. */
551 private boolean mSuppressCaptureIndicator;
554 * Provides current preview frame and the controls/overlay from the module that
555 * are shown on top of the preview.
557 public interface CameraModuleScreenShotProvider {
559 * Returns the current preview frame down-sampled using the given down-sample
562 * @param downSampleFactor the down sample factor for down sampling the
563 * preview frame. (e.g. a down sample factor of
564 * 2 means to scale down the preview frame to 1/2
565 * the width and height.)
566 * @return down-sampled preview frame
568 public Bitmap getPreviewFrame(int downSampleFactor);
571 * @return the controls and overlays that are currently showing on top of
572 * the preview drawn into a bitmap with no scaling applied.
574 public Bitmap getPreviewOverlayAndControls();
577 * Returns a bitmap containing the current screenshot.
579 * @param previewDownSampleFactor the downsample factor applied on the
580 * preview frame when taking the screenshot
582 public Bitmap getScreenShot(int previewDownSampleFactor);
586 * This listener gets called when the size of the window (excluding the system
587 * decor such as status bar and nav bar) has changed.
589 public interface NonDecorWindowSizeChangedListener {
590 public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
593 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
594 new CameraModuleScreenShotProvider() {
596 public Bitmap getPreviewFrame(int downSampleFactor) {
597 if (mCameraRootView == null || mTextureView == null) {
600 // Gets the bitmap from the preview TextureView.
601 Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
606 public Bitmap getPreviewOverlayAndControls() {
607 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
608 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
609 Canvas canvas = new Canvas(overlays);
610 mCameraRootView.draw(canvas);
615 public Bitmap getScreenShot(int previewDownSampleFactor) {
616 Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
617 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
618 Canvas canvas = new Canvas(screenshot);
619 canvas.drawARGB(255, 0, 0, 0);
620 Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
621 if (preview != null) {
622 canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
624 Bitmap overlay = getPreviewOverlayAndControls();
625 if (overlay != null) {
626 canvas.drawBitmap(overlay, 0f, 0f, null);
632 private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
634 public long getCoverHiddenTime() {
635 return mCoverHiddenTime;
639 * This resets the preview to have no applied transform matrix.
641 public void clearPreviewTransform() {
642 mTextureViewHelper.clearTransform();
645 public void updatePreviewAspectRatio(float aspectRatio) {
646 mTextureViewHelper.updateAspectRatio(aspectRatio);
650 * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
651 * its TextureView. This is necessary to get the expected behavior for the TextureView's
652 * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
653 * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
656 * b/17286155 - Tracking a fix for this in HardwareLayer.
658 public void setDefaultBufferSizeToViewDimens() {
659 if (mSurface == null || mTextureView == null) {
660 Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
663 mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
667 * Updates the preview matrix without altering it.
670 * @param aspectRatio the desired aspect ratio for the preview.
672 public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
673 mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
677 * @return the rect that will display the preview.
679 public RectF getFullscreenRect() {
680 return mTextureViewHelper.getFullscreenRect();
684 * This is to support modules that calculate their own transform matrix because
685 * they need to use a transform matrix to rotate the preview.
687 * @param matrix transform matrix to be set on the TextureView
689 public void updatePreviewTransform(Matrix matrix) {
690 mTextureViewHelper.updateTransform(matrix);
693 public interface AnimationFinishedListener {
694 public void onAnimationFinished(boolean success);
697 private class MyTouchListener implements View.OnTouchListener {
698 private boolean mScaleStarted = false;
700 public boolean onTouch(View v, MotionEvent event) {
701 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
702 mScaleStarted = false;
703 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
704 mScaleStarted = true;
706 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
711 * This gesture listener finds out the direction of the scroll gestures and
712 * sends them to CameraAppUI to do further handling.
714 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
715 private MotionEvent mDown;
718 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
719 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
720 || mSwipeState != IDLE
726 int deltaX = (int) (ev.getX() - mDown.getX());
727 int deltaY = (int) (ev.getY() - mDown.getY());
728 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
729 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
730 // Calculate the direction of the swipe.
731 if (deltaX >= Math.abs(deltaY)) {
733 setSwipeState(SWIPE_RIGHT);
734 } else if (deltaX <= -Math.abs(deltaY)) {
736 setSwipeState(SWIPE_LEFT);
743 private void setSwipeState(int swipeState) {
744 mSwipeState = swipeState;
745 // Notify new swipe detected.
746 onSwipeDetected(swipeState);
750 public boolean onDown(MotionEvent ev) {
751 mDown = MotionEvent.obtain(ev);
757 public CameraAppUI(AppController controller, MainActivityLayout appRootView,
758 boolean isCaptureIntent) {
759 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
760 mController = controller;
761 mIsCaptureIntent = isCaptureIntent;
763 mAppRootView = appRootView;
764 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
765 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
766 mModeTransitionView = (ModeTransitionView)
767 mAppRootView.findViewById(R.id.mode_transition_view);
768 mFilmstripBottomControls = new FilmstripBottomPanel(controller,
769 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
770 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
771 mGestureDetector = new GestureDetector(controller.getAndroidContext(),
772 new MyGestureListener());
773 Resources res = controller.getAndroidContext().getResources();
774 mCaptureLayoutHelper = new CaptureLayoutHelper(
775 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
776 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
777 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
778 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
779 if (mModeListView != null) {
780 mModeListView.setModeSwitchListener(this);
781 mModeListView.setModeListOpenListener(this);
782 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
783 mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
784 boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
785 SettingsManager.SCOPE_GLOBAL,
786 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
787 mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
789 Log.e(TAG, "Cannot find mode list in the view hierarchy");
791 mAnimationManager = new AnimationManager();
792 mRoundedThumbnailView = (RoundedThumbnailView) appRootView.findViewById(R.id.rounded_thumbnail_view);
793 mRoundedThumbnailView.setCallback(new RoundedThumbnailView.Callback() {
795 public void onHitStateFinished() {
796 mFilmstripLayout.showFilmstrip();
800 mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
801 initDisplayListener();
802 mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
803 View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
804 modeListToggle.setOnClickListener(new View.OnClickListener() {
806 public void onClick(View view) {
810 View filmstripToggle = mAppRootView.findViewById(
811 R.id.accessibility_filmstrip_toggle_button);
812 filmstripToggle.setOnClickListener(new View.OnClickListener() {
814 public void onClick(View view) {
819 mSuppressCaptureIndicator = false;
824 * Freeze what is currently shown on screen until the next preview frame comes
827 public void freezeScreenUntilPreviewReady() {
828 Log.v(TAG, "freezeScreenUntilPreviewReady");
829 mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
830 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
831 mHideCoverRunnable = new Runnable() {
834 mModeTransitionView.hideImageCover();
837 mModeCoverState = COVER_SHOWN;
841 * Creates a cling for the specific viewer and links the cling to the corresponding
842 * button for layout position.
844 * @param viewerType defines which viewer the cling is for.
846 public void setupClingForViewer(int viewerType) {
847 if (viewerType == BottomPanel.VIEWER_REFOCUS) {
848 FrameLayout filmstripContent = (FrameLayout) mAppRootView
849 .findViewById(R.id.camera_filmstrip_content_layout);
850 if (filmstripContent != null) {
851 // Creates refocus cling.
852 LayoutInflater inflater = AndroidServices.instance().provideLayoutInflater();
853 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
854 // Sets instruction text in the cling.
855 refocusCling.setText(mController.getAndroidContext().getResources()
856 .getString(R.string.cling_text_for_refocus_editor_button));
858 // Adds cling into view hierarchy.
859 int clingWidth = mController.getAndroidContext()
860 .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
861 filmstripContent.addView(refocusCling, clingWidth,
862 ViewGroup.LayoutParams.WRAP_CONTENT);
863 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
869 * Clears the listeners for the cling and remove it from the view hierarchy.
871 * @param viewerType defines which viewer the cling is for.
873 public void clearClingForViewer(int viewerType) {
874 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
875 if (clingToBeRemoved == null) {
876 // No cling is created for the specific viewer type.
879 mFilmstripBottomControls.clearClingForViewer(viewerType);
880 clingToBeRemoved.setVisibility(View.GONE);
881 mAppRootView.removeView(clingToBeRemoved);
885 * Enable or disable swipe gestures. We want to disable them e.g. while we
888 public void setSwipeEnabled(boolean enabled) {
889 mSwipeEnabled = enabled;
890 // TODO: This can be removed once we come up with a new design for handling swipe
891 // on shutter button and mode options. (More details: b/13751653)
892 mAppRootView.setSwipeEnabled(enabled);
895 public void onDestroy() {
896 AndroidServices.instance().provideDisplayManager()
897 .unregisterDisplayListener(mDisplayListener);
901 * Initializes the display listener to listen to display changes such as
902 * 180-degree rotation change, which will not have an onConfigurationChanged
905 private void initDisplayListener() {
906 if (ApiHelper.HAS_DISPLAY_LISTENER) {
907 mLastRotation = CameraUtil.getDisplayRotation();
909 mDisplayListener = new DisplayManager.DisplayListener() {
911 public void onDisplayAdded(int arg0) {
916 public void onDisplayChanged(int displayId) {
917 int rotation = CameraUtil.getDisplayRotation(
919 if ((rotation - mLastRotation + 360) % 360 == 180
920 && mPreviewStatusListener != null) {
921 mPreviewStatusListener.onPreviewFlipped();
922 mStickyBottomCaptureLayout.requestLayout();
923 mModeListView.requestLayout();
924 mTextureView.requestLayout();
926 mLastRotation = rotation;
930 public void onDisplayRemoved(int arg0) {
935 AndroidServices.instance().provideDisplayManager()
936 .registerDisplayListener(mDisplayListener, null);
941 * Redirects touch events to appropriate recipient views based on swipe direction.
942 * More specifically, swipe up and swipe down will be handled by the view that handles
943 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
944 * to mode list in order to bring up mode list.
946 private void onSwipeDetected(int swipeState) {
947 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
948 // TODO: Polish quick switch after this release.
949 // Quick switch between modes.
950 int currentModuleIndex = mController.getCurrentModuleIndex();
951 final int moduleToTransitionTo =
952 mController.getQuickSwitchToModuleId(currentModuleIndex);
953 if (currentModuleIndex != moduleToTransitionTo) {
954 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
955 int shadeColorId = R.color.camera_gray_background;
956 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
957 mController.getAndroidContext());
959 AnimationFinishedListener listener = new AnimationFinishedListener() {
961 public void onAnimationFinished(boolean success) {
963 mHideCoverRunnable = new Runnable() {
966 mModeTransitionView.startPeepHoleAnimation();
969 mModeCoverState = COVER_SHOWN;
970 // Go to new module when the previous operation is successful.
971 mController.onModeSelected(moduleToTransitionTo);
976 } else if (swipeState == SWIPE_LEFT) {
977 // Pass the touch sequence to filmstrip layout.
978 mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
979 } else if (swipeState == SWIPE_RIGHT) {
980 // Pass the touch to mode switcher
981 mAppRootView.redirectTouchEventsTo(mModeListView);
986 * Gets called when activity resumes in preview.
988 public void resume() {
989 // Show mode theme cover until preview is ready
990 showModeCoverUntilPreviewReady();
992 // Hide action bar first since we are in full screen mode first, and
993 // switch the system UI to lights-out mode.
994 mFilmstripPanel.hide();
996 // Show UI that is meant to only be used when spoken feedback is
998 mAccessibilityAffordances.setVisibility(
999 (!mIsCaptureIntent && mAccessibilityUtil.isAccessibilityEnabled()) ? View.VISIBLE
1004 * Opens the mode list (e.g. because of the menu button being pressed) and
1005 * adapts the rest of the UI.
1007 public void openModeList() {
1008 mModeOptionsOverlay.closeModeOptions();
1009 mModeListView.onMenuPressed();
1012 public void showAccessibilityZoomUI(float maxZoom) {
1013 mAccessibilityUtil.showZoomUI(maxZoom);
1016 public void hideAccessibilityZoomUI() {
1017 mAccessibilityUtil.hideZoomUI();
1021 * A cover view showing the mode theme color and mode icon will be visible on
1022 * top of preview until preview is ready (i.e. camera preview is started and
1023 * the first frame has been received).
1025 private void showModeCoverUntilPreviewReady() {
1026 int modeId = mController.getCurrentModuleIndex();
1027 int colorId = R.color.camera_gray_background;;
1028 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1029 mModeTransitionView.setupModeCover(colorId, iconId);
1030 mHideCoverRunnable = new Runnable() {
1033 mModeTransitionView.hideModeCover(null);
1034 if (!mDisableAllUserInteractions) {
1035 showShimmyDelayed();
1039 mModeCoverState = COVER_SHOWN;
1042 private void showShimmyDelayed() {
1043 if (!mIsCaptureIntent) {
1044 // Show shimmy in SHIMMY_DELAY_MS
1045 mModeListView.showModeSwitcherHint();
1049 private void hideModeCover() {
1050 if (mHideCoverRunnable != null) {
1051 mAppRootView.post(mHideCoverRunnable);
1052 mHideCoverRunnable = null;
1054 mModeCoverState = COVER_HIDDEN;
1055 if (mCoverHiddenTime < 0) {
1056 mCoverHiddenTime = System.currentTimeMillis();
1061 public void onPreviewVisiblityChanged(int visibility) {
1062 if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1063 setIndicatorBottomBarWrapperVisible(false);
1064 mAccessibilityAffordances.setVisibility(View.GONE);
1066 setIndicatorBottomBarWrapperVisible(true);
1067 if (!mIsCaptureIntent && mAccessibilityUtil.isAccessibilityEnabled()) {
1068 mAccessibilityAffordances.setVisibility(View.VISIBLE);
1070 mAccessibilityAffordances.setVisibility(View.GONE);
1076 * Call to stop the preview from being rendered. Sets the entire capture
1077 * root view to invisible which includes the preview plus focus indicator
1078 * and any other auxiliary views for capture modes.
1080 public void pausePreviewRendering() {
1081 mCameraRootView.setVisibility(View.INVISIBLE);
1085 * Call to begin rendering the preview and auxiliary views again.
1087 public void resumePreviewRendering() {
1088 mCameraRootView.setVisibility(View.VISIBLE);
1092 * Returns the transform associated with the preview view.
1094 * @param m the Matrix in which to copy the current transform.
1095 * @return The specified matrix if not null or a new Matrix instance
1098 public Matrix getPreviewTransform(Matrix m) {
1099 return mTextureView.getTransform(m);
1103 public void onOpenFullScreen() {
1108 public void onModeListOpenProgress(float progress) {
1109 // When the mode list is in transition, ensure the large layers are
1110 // hardware accelerated.
1111 if (progress >= 1.0f || progress <= 0.0f) {
1112 // Convert hardware layers back to default layer types when animation stops
1113 // to prevent accidental artifacting.
1114 if(mModeOptionsToggle.getLayerType() == View.LAYER_TYPE_HARDWARE ||
1115 mShutterButton.getLayerType() == View.LAYER_TYPE_HARDWARE) {
1116 Log.v(TAG, "Disabling hardware layer for the Mode Options Toggle Button.");
1117 mModeOptionsToggle.setLayerType(View.LAYER_TYPE_NONE, null);
1118 Log.v(TAG, "Disabling hardware layer for the Shutter Button.");
1119 mShutterButton.setLayerType(View.LAYER_TYPE_NONE, null);
1122 if(mModeOptionsToggle.getLayerType() != View.LAYER_TYPE_HARDWARE ||
1123 mShutterButton.getLayerType() != View.LAYER_TYPE_HARDWARE) {
1124 Log.v(TAG, "Enabling hardware layer for the Mode Options Toggle Button.");
1125 mModeOptionsToggle.setLayerType(View.LAYER_TYPE_HARDWARE, null);
1126 Log.v(TAG, "Enabling hardware layer for the Shutter Button.");
1127 mShutterButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
1131 progress = 1 - progress;
1132 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1133 mModeOptionsToggle.setAlpha(interpolatedProgress);
1134 // Change shutter button alpha linearly based on the mode list open progress:
1135 // set the alpha to disabled alpha when list is fully open, to enabled alpha
1136 // when the list is fully closed.
1137 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1138 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1142 public void onModeListClosed() {
1143 // Convert hardware layers back to default layer types when animation stops
1144 // to prevent accidental artifacting.
1145 if(mModeOptionsToggle.getLayerType() == View.LAYER_TYPE_HARDWARE ||
1146 mShutterButton.getLayerType() == View.LAYER_TYPE_HARDWARE) {
1147 Log.v(TAG, "Disabling hardware layer for the Mode Options Toggle Button.");
1148 mModeOptionsToggle.setLayerType(View.LAYER_TYPE_NONE, null);
1149 Log.v(TAG, "Disabling hardware layer for the Shutter Button.");
1150 mShutterButton.setLayerType(View.LAYER_TYPE_NONE, null);
1153 // Make sure the alpha on mode options ellipse is reset when mode drawer
1155 mModeOptionsToggle.setAlpha(1f);
1156 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1160 * Called when the back key is pressed.
1162 * @return Whether the UI responded to the key event.
1164 public boolean onBackPressed() {
1165 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1166 return mFilmstripLayout.onBackPressed();
1168 return mModeListView.onBackPressed();
1173 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1174 * listens to SurfaceTexture changes. In addition, listeners are set on
1175 * dependent app ui elements.
1177 * @param previewStatusListener the listener that gets notified when SurfaceTexture
1180 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1181 mPreviewStatusListener = previewStatusListener;
1182 if (mPreviewStatusListener != null) {
1183 onPreviewListenerChanged();
1188 * When the PreviewStatusListener changes, listeners need to be
1189 * set on the following app ui elements:
1190 * {@link com.android.camera.ui.PreviewOverlay},
1191 * {@link com.android.camera.ui.BottomBar},
1192 * {@link com.android.camera.ui.IndicatorIconController}.
1194 private void onPreviewListenerChanged() {
1195 // Set a listener for recognizing preview gestures.
1196 GestureDetector.OnGestureListener gestureListener
1197 = mPreviewStatusListener.getGestureListener();
1198 if (gestureListener != null) {
1199 mPreviewOverlay.setGestureListener(gestureListener);
1201 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1202 if (touchListener != null) {
1203 mPreviewOverlay.setTouchListener(touchListener);
1206 mTextureViewHelper.setAutoAdjustTransform(
1207 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1211 * This method should be called in onCameraOpened. It defines CameraAppUI
1212 * specific changes that depend on the camera or camera settings.
1214 public void onChangeCamera() {
1215 ModuleController moduleController = mController.getCurrentModuleController();
1216 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1218 if (mIndicatorIconController != null) {
1219 // Sync the settings state with the indicator state.
1220 mIndicatorIconController.syncIndicators();
1225 * Adds a listener to receive callbacks when preview area changes.
1227 public void addPreviewAreaChangedListener(
1228 PreviewStatusListener.PreviewAreaChangedListener listener) {
1229 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1233 * Removes a listener that receives callbacks when preview area changes.
1235 public void removePreviewAreaChangedListener(
1236 PreviewStatusListener.PreviewAreaChangedListener listener) {
1237 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1241 * This inflates generic_module layout, which contains all the shared views across
1242 * modules. Then each module inflates their own views in the given view group. For
1243 * now, this is called every time switching from a not-yet-refactored module to a
1244 * refactored module. In the future, this should only need to be done once per app
1247 public void prepareModuleUI() {
1248 mController.getSettingsManager().addListener(this);
1249 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1250 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1251 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1252 mController.getCameraProvider());
1253 mTextureViewHelper.setSurfaceTextureListener(this);
1254 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1256 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1257 int unpressedColor = mController.getAndroidContext().getResources()
1258 .getColor(R.color.camera_gray_background);
1259 setBottomBarColor(unpressedColor);
1260 updateModeSpecificUIColors();
1262 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1265 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1267 // Sets the visibility of the bottom bar and the mode options.
1268 resetBottomControls(mController.getCurrentModuleController(),
1269 mController.getCurrentModuleIndex());
1270 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1272 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1273 addShutterListener(mController.getCurrentModuleController());
1274 addShutterListener(mModeOptionsOverlay);
1275 addShutterListener(this);
1277 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1278 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1280 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1281 mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1282 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1283 mAccessibilityUtil = new AccessibilityUtil(mPreviewOverlay, mAccessibilityAffordances);
1285 mCaptureOverlay = (CaptureAnimationOverlay)
1286 mCameraRootView.findViewById(R.id.capture_overlay);
1287 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1288 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1290 if (mIndicatorIconController == null) {
1291 mIndicatorIconController =
1292 new IndicatorIconController(mController, mAppRootView);
1295 mController.getButtonManager().load(mCameraRootView);
1296 mController.getButtonManager().setListener(mIndicatorIconController);
1297 mController.getSettingsManager().addListener(mIndicatorIconController);
1299 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1300 mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1301 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1302 .findViewById(R.id.tutorials_placeholder_wrapper);
1303 mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1304 .findViewById(R.id.sticky_bottom_capture_layout);
1305 mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1306 mCountdownCancelButton = (ImageButton) mStickyBottomCaptureLayout
1307 .findViewById(R.id.shutter_cancel_button);
1309 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1310 mTextureViewHelper.addAspectRatioChangedListener(
1311 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1313 public void onPreviewAspectRatioChanged(float aspectRatio) {
1314 mModeOptionsOverlay.requestLayout();
1315 mBottomBar.requestLayout();
1322 * Called indirectly from each module in their initialization to get a view group
1323 * to inflate the module specific views in.
1325 * @return a view group for modules to attach views to
1327 public FrameLayout getModuleRootView() {
1328 // TODO: Change it to mModuleUI when refactor is done
1329 return mCameraRootView;
1333 * Remove all the module specific views.
1335 public void clearModuleUI() {
1336 if (mModuleUI != null) {
1337 mModuleUI.removeAllViews();
1339 removeShutterListener(mController.getCurrentModuleController());
1340 mTutorialsPlaceHolderWrapper.removeAllViews();
1341 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1343 setShutterButtonEnabled(true);
1344 mPreviewStatusListener = null;
1345 mPreviewOverlay.reset();
1347 Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1348 mFocusRing.stopFocusAnimations();
1352 * Gets called when preview is ready to start. It sets up one shot preview callback
1353 * in order to receive a callback when the preview frame is available, so that
1354 * the preview cover can be hidden to reveal preview.
1356 * An alternative for getting the timing to hide preview cover is through
1357 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1358 * which is less accurate but therefore is the fallback for modules that manage
1359 * their own preview callbacks (as setting one preview callback will override
1360 * any other installed preview callbacks), or use camera2 API.
1362 public void onPreviewReadyToStart() {
1363 if (mModeCoverState == COVER_SHOWN) {
1364 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1365 mController.setupOneShotPreviewListener();
1370 * Gets called when preview is started.
1372 public void onPreviewStarted() {
1373 Log.v(TAG, "onPreviewStarted");
1374 if (mModeCoverState == COVER_SHOWN) {
1375 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1377 enableModeOptions();
1381 * Gets notified when next preview frame comes in.
1383 public void onNewPreviewFrame() {
1384 Log.v(TAG, "onNewPreviewFrame");
1385 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1390 public void onShutterButtonClick() {
1392 * Set the mode options toggle unclickable, generally
1393 * throughout the app, whenever the shutter button is clicked.
1395 * This could be done in the OnShutterButtonListener of the
1396 * ModeOptionsOverlay, but since it is very important that we
1397 * can clearly see when the toggle becomes clickable again,
1398 * keep all of that logic at this level.
1400 // disableModeOptions();
1404 public void onShutterCoordinate(TouchCoordinate coord) {
1409 public void onShutterButtonFocus(boolean pressed) {
1414 public void onShutterButtonLongPressed() {
1419 * Set the mode options toggle clickable.
1421 public void enableModeOptions() {
1423 * For modules using camera 1 api, this gets called in
1424 * onSurfaceTextureUpdated whenever the preview gets stopped and
1425 * started after each capture. This also takes care of the
1426 * case where the mode options might be unclickable when we
1429 * For modules using camera 2 api, they're required to call this
1430 * method when a capture is "completed". Unfortunately this differs
1431 * per module implementation.
1433 if (!mDisableAllUserInteractions) {
1434 mModeOptionsOverlay.setToggleClickable(true);
1439 * Set the mode options toggle not clickable.
1441 public void disableModeOptions() {
1442 mModeOptionsOverlay.setToggleClickable(false);
1445 public void setDisableAllUserInteractions(boolean disable) {
1447 disableModeOptions();
1448 setShutterButtonEnabled(false);
1449 setSwipeEnabled(false);
1450 mModeListView.hideAnimated();
1452 enableModeOptions();
1453 setShutterButtonEnabled(true);
1454 setSwipeEnabled(true);
1456 mDisableAllUserInteractions = disable;
1460 public void onModeButtonPressed(int modeIndex) {
1461 // TODO: Make CameraActivity listen to ModeListView's events.
1462 int pressedModuleId = mController.getModuleId(modeIndex);
1463 int currentModuleId = mController.getCurrentModuleIndex();
1464 if (pressedModuleId != currentModuleId) {
1465 hideCaptureIndicator();
1470 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1472 * @param modeIndex mode index of the selected mode
1475 public void onModeSelected(int modeIndex) {
1476 mHideCoverRunnable = new Runnable() {
1479 mModeListView.startModeSelectionAnimation();
1482 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1483 mModeCoverState = COVER_SHOWN;
1485 int lastIndex = mController.getCurrentModuleIndex();
1486 // Actual mode teardown / new mode initialization happens here
1487 mController.onModeSelected(modeIndex);
1488 int currentIndex = mController.getCurrentModuleIndex();
1490 if (lastIndex == currentIndex) {
1494 updateModeSpecificUIColors();
1497 private void updateModeSpecificUIColors() {
1498 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1502 public void onSettingsSelected() {
1503 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1504 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1505 mModeListView.setShouldShowSettingsCling(false);
1506 mController.onSettingsSelected();
1510 public int getCurrentModeIndex() {
1511 return mController.getCurrentModuleIndex();
1514 /********************** Capture animation **********************/
1515 /* TODO: This session is subject to UX changes. In addition to the generic
1516 flash animation and post capture animation, consider designating a parameter
1517 for specifying the type of animation, as well as an animation finished listener
1518 so that modules can have more knowledge of the status of the animation. */
1521 * Turns on or off the capture indicator suppression.
1523 public void setShouldSuppressCaptureIndicator(boolean suppress) {
1524 mSuppressCaptureIndicator = suppress;
1528 * Starts the capture indicator pop-out animation.
1530 * @param accessibilityString An accessibility String to be announced during the peek animation.
1532 public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1533 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
1536 mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1540 * Updates the thumbnail image in the capture indicator.
1542 * @param thumbnailBitmap The thumbnail image to be shown.
1544 public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
1545 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
1548 mRoundedThumbnailView.setThumbnail(thumbnailBitmap, rotation);
1552 * Hides the capture indicator.
1554 public void hideCaptureIndicator() {
1555 mRoundedThumbnailView.hideThumbnail();
1559 * Starts the flash animation.
1561 public void startFlashAnimation(boolean shortFlash) {
1562 mCaptureOverlay.startFlashAnimation(shortFlash);
1566 * Cancels the pre-capture animation.
1568 public void cancelPreCaptureAnimation() {
1569 mAnimationManager.cancelAnimations();
1573 * Cancels the post-capture animation.
1575 public void cancelPostCaptureAnimation() {
1576 mAnimationManager.cancelAnimations();
1579 public FilmstripContentPanel getFilmstripContentPanel() {
1580 return mFilmstripPanel;
1584 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1585 * bottom of the filmstrip.
1587 public BottomPanel getFilmstripBottomControls() {
1588 return mFilmstripBottomControls;
1591 public void showBottomControls() {
1592 mFilmstripBottomControls.show();
1595 public void hideBottomControls() {
1596 mFilmstripBottomControls.hide();
1600 * @param listener The listener for bottom controls.
1602 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1603 mFilmstripBottomControls.setListener(listener);
1606 /***************************SurfaceTexture Api and Listener*********************************/
1609 * Return the shared surface texture.
1611 public SurfaceTexture getSurfaceTexture() {
1616 * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1618 public int getSurfaceWidth() {
1619 return mSurfaceWidth;
1623 * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1625 public int getSurfaceHeight() {
1626 return mSurfaceHeight;
1630 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1632 mSurfaceWidth = width;
1633 mSurfaceHeight = height;
1634 Log.v(TAG, "SurfaceTexture is available");
1635 if (mPreviewStatusListener != null) {
1636 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1638 enableModeOptions();
1642 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1644 mSurfaceWidth = width;
1645 mSurfaceHeight = height;
1646 if (mPreviewStatusListener != null) {
1647 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1652 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1654 Log.v(TAG, "SurfaceTexture is destroyed");
1655 if (mPreviewStatusListener != null) {
1656 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1662 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1664 if (mPreviewStatusListener != null) {
1665 mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1667 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1668 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1669 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1674 /****************************Grid lines api ******************************/
1677 * Show a set of evenly spaced lines over the preview. The number
1678 * of lines horizontally and vertically is determined by
1679 * {@link com.android.camera.ui.GridLines}.
1681 public void showGridLines() {
1682 if (mGridLines != null) {
1683 mGridLines.setVisibility(View.VISIBLE);
1688 * Hide the set of evenly spaced grid lines overlaying the preview.
1690 public void hideGridLines() {
1691 if (mGridLines != null) {
1692 mGridLines.setVisibility(View.INVISIBLE);
1697 * Return a callback which shows or hide the preview grid lines
1698 * depending on whether the grid lines setting is set on.
1700 public ButtonManager.ButtonCallback getGridLinesCallback() {
1701 return new ButtonManager.ButtonCallback() {
1703 public void onStateChanged(int state) {
1704 if (!mController.isPaused()) {
1705 if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1715 /***************************Mode options api *****************************/
1718 * Set the mode options visible.
1720 public void showModeOptions() {
1721 /* Make mode options clickable. */
1722 enableModeOptions();
1723 mModeOptionsOverlay.setVisibility(View.VISIBLE);
1727 * Set the mode options invisible. This is necessary for modes
1728 * that don't show a bottom bar for the capture UI.
1730 public void hideModeOptions() {
1731 mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1734 /****************************Bottom bar api ******************************/
1737 * Sets up the bottom bar and mode options with the correct
1738 * shutter button and visibility based on the current module.
1740 public void resetBottomControls(ModuleController module, int moduleIndex) {
1741 if (areBottomControlsUsed(module)) {
1742 setBottomBarShutterIcon(moduleIndex);
1743 mCaptureLayoutHelper.setShowBottomBar(true);
1745 mCaptureLayoutHelper.setShowBottomBar(false);
1750 * Show or hide the mode options and bottom bar, based on
1751 * whether the current module is using the bottom bar. Returns
1752 * whether the mode options and bottom bar are used.
1754 private boolean areBottomControlsUsed(ModuleController module) {
1755 if (module.isUsingBottomBar()) {
1767 * Set the bottom bar visible.
1769 public void showBottomBar() {
1770 mBottomBar.setVisibility(View.VISIBLE);
1774 * Set the bottom bar invisible.
1776 public void hideBottomBar() {
1777 mBottomBar.setVisibility(View.INVISIBLE);
1781 * Sets the color of the bottom bar.
1783 public void setBottomBarColor(int colorId) {
1784 mBottomBar.setBackgroundColor(colorId);
1788 * Sets the pressed color of the bottom bar for a camera mode index.
1790 public void setBottomBarColorsForModeIndex(int index) {
1791 mBottomBar.setColorsForModeIndex(index);
1795 * Sets the shutter button icon on the bottom bar, based on
1798 public void setBottomBarShutterIcon(int modeIndex) {
1799 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1800 mController.getAndroidContext());
1801 mBottomBar.setShutterButtonIcon(shutterIconId);
1804 public void animateBottomBarToVideoStop(int shutterIconId) {
1805 mBottomBar.animateToVideoStop(shutterIconId);
1808 public void animateBottomBarToFullSize(int shutterIconId) {
1809 mBottomBar.animateToFullSize(shutterIconId);
1812 public void setShutterButtonEnabled(final boolean enabled) {
1813 if (!mDisableAllUserInteractions) {
1814 mBottomBar.post(new Runnable() {
1817 mBottomBar.setShutterButtonEnabled(enabled);
1823 public void setShutterButtonImportantToA11y(boolean important) {
1824 mBottomBar.setShutterButtonImportantToA11y(important);
1827 public boolean isShutterButtonEnabled() {
1828 return mBottomBar.isShutterButtonEnabled();
1831 public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1832 mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1836 * Set the visibility of the bottom bar.
1838 // TODO: needed for when panorama is managed by the generic module ui.
1839 public void setBottomBarVisible(boolean visible) {
1840 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1844 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1846 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1847 mShutterButton.addOnShutterButtonListener(listener);
1851 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1853 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1854 mShutterButton.removeOnShutterButtonListener(listener);
1858 * Sets or replaces the "cancel shutter" button listener.
1860 * TODO: Make this part of the interface the same way shutter button
1863 public void setCancelShutterButtonListener(View.OnClickListener listener) {
1864 mCountdownCancelButton.setOnClickListener(listener);
1868 * Performs a transition to the capture layout of the bottom bar.
1870 public void transitionToCapture() {
1871 ModuleController moduleController = mController.getCurrentModuleController();
1872 applyModuleSpecs(moduleController.getHardwareSpec(),
1873 moduleController.getBottomBarSpec());
1874 mBottomBar.transitionToCapture();
1879 * Displays the Cancel button instead of the capture button.
1881 public void transitionToCancel() {
1882 ModuleController moduleController = mController.getCurrentModuleController();
1883 applyModuleSpecs(moduleController.getHardwareSpec(),
1884 moduleController.getBottomBarSpec());
1885 mBottomBar.transitionToCancel();
1890 * Performs a transition to the global intent layout.
1892 public void transitionToIntentCaptureLayout() {
1893 ModuleController moduleController = mController.getCurrentModuleController();
1894 applyModuleSpecs(moduleController.getHardwareSpec(),
1895 moduleController.getBottomBarSpec());
1896 mBottomBar.transitionToIntentCaptureLayout();
1901 * Performs a transition to the global intent review layout.
1903 public void transitionToIntentReviewLayout() {
1904 ModuleController moduleController = mController.getCurrentModuleController();
1905 applyModuleSpecs(moduleController.getHardwareSpec(),
1906 moduleController.getBottomBarSpec());
1907 mBottomBar.transitionToIntentReviewLayout();
1912 * @return whether UI is in intent review mode
1914 public boolean isInIntentReview() {
1915 return mBottomBar.isInIntentReview();
1919 public void onSettingChanged(SettingsManager settingsManager, String key) {
1920 // Update the mode options based on the hardware spec,
1921 // when hdr changes to prevent flash from getting out of sync.
1922 if (key.equals(Keys.KEY_CAMERA_HDR)) {
1923 ModuleController moduleController = mController.getCurrentModuleController();
1924 applyModuleSpecs(moduleController.getHardwareSpec(),
1925 moduleController.getBottomBarSpec());
1930 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1931 * to the bottom bar mode options based on limitations from a
1932 * {@link com.android.camera.hardware.HardwareSpec}.
1934 * Options not supported by the hardware are either hidden
1935 * or disabled, depending on the option.
1937 * Otherwise, the option is fully enabled and clickable.
1939 public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1940 final BottomBarUISpec bottomBarSpec) {
1941 if (hardwareSpec == null || bottomBarSpec == null) {
1945 ButtonManager buttonManager = mController.getButtonManager();
1946 SettingsManager settingsManager = mController.getSettingsManager();
1948 buttonManager.setToInitialState();
1950 /** Standard mode options */
1951 if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1952 hardwareSpec.isFrontCameraSupported()) {
1953 if (bottomBarSpec.enableCamera) {
1954 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1955 bottomBarSpec.cameraCallback);
1957 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1960 // Hide camera icon if front camera not available.
1961 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1964 if (bottomBarSpec.hideFlash) {
1965 // Hide both flash and torch button in flash disable logic
1966 buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1967 buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1969 if (hardwareSpec.isFlashSupported()) {
1970 if (bottomBarSpec.enableFlash) {
1971 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1972 bottomBarSpec.flashCallback);
1973 } else if (bottomBarSpec.enableTorchFlash) {
1974 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1975 bottomBarSpec.flashCallback);
1976 } else if (bottomBarSpec.enableHdrPlusFlash) {
1977 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1978 bottomBarSpec.flashCallback);
1980 // Hide both flash and torch button in flash disable logic
1981 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1982 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1985 // Disable both flash and torch icon if not supported
1986 // by the chosen camera hardware.
1987 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1988 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1992 if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1993 // Force hide hdr or hdr plus icon.
1994 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1996 if (hardwareSpec.isHdrPlusSupported()) {
1997 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1998 mController.getModuleScope())) {
1999 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
2000 bottomBarSpec.hdrCallback);
2002 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
2004 } else if (hardwareSpec.isHdrSupported()) {
2005 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
2006 mController.getModuleScope())) {
2007 buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
2008 bottomBarSpec.hdrCallback);
2010 buttonManager.disableButton(ButtonManager.BUTTON_HDR);
2013 // Hide hdr plus or hdr icon if neither are supported.
2014 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
2018 if (bottomBarSpec.hideGridLines) {
2019 // Force hide grid lines icon.
2020 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
2023 if (bottomBarSpec.enableGridLines) {
2024 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
2025 bottomBarSpec.gridLinesCallback != null ?
2026 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
2029 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
2034 if (bottomBarSpec.enableSelfTimer) {
2035 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
2037 if (bottomBarSpec.showSelfTimer) {
2038 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
2040 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
2044 if (bottomBarSpec.enablePanoOrientation
2045 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
2046 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
2049 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
2050 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
2051 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
2052 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
2053 if (enableExposureCompensation) {
2054 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION,
2055 new View.OnClickListener() {
2057 public void onClick(View v) {
2058 mModeOptionsOverlay.showExposureOptions();
2061 buttonManager.setExposureCompensationParameters(
2062 bottomBarSpec.minExposureCompensation,
2063 bottomBarSpec.maxExposureCompensation,
2064 bottomBarSpec.exposureCompensationStep);
2066 buttonManager.setExposureCompensationCallback(
2067 bottomBarSpec.exposureCompensationSetCallback);
2068 buttonManager.updateExposureButtons();
2070 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2071 buttonManager.setExposureCompensationCallback(null);
2075 if (bottomBarSpec.showCancel) {
2076 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2077 bottomBarSpec.cancelCallback);
2079 if (bottomBarSpec.showDone) {
2080 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2081 bottomBarSpec.doneCallback);
2083 if (bottomBarSpec.showRetake) {
2084 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2085 bottomBarSpec.retakeCallback,
2087 R.string.retake_button_description);
2089 if (bottomBarSpec.showReview) {
2090 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2091 bottomBarSpec.reviewCallback,
2093 R.string.review_button_description);
2098 * Shows the given tutorial on the screen.
2100 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2101 tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2104 /***************************Filmstrip api *****************************/
2106 public void showFilmstrip() {
2107 mModeListView.onBackPressed();
2108 mFilmstripLayout.showFilmstrip();
2111 public void hideFilmstrip() {
2112 mFilmstripLayout.hideFilmstrip();