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.accessibilityservice.AccessibilityServiceInfo;
20 import android.content.res.Resources;
21 import android.graphics.Bitmap;
22 import android.graphics.Canvas;
23 import android.graphics.Color;
24 import android.graphics.Matrix;
25 import android.graphics.RectF;
26 import android.graphics.SurfaceTexture;
27 import android.hardware.display.DisplayManager;
28 import android.util.CameraPerformanceTracker;
29 import android.view.GestureDetector;
30 import android.view.LayoutInflater;
31 import android.view.MotionEvent;
32 import android.view.TextureView;
33 import android.view.View;
34 import android.view.ViewConfiguration;
35 import android.view.ViewGroup;
36 import android.view.accessibility.AccessibilityManager;
37 import android.widget.FrameLayout;
39 import com.android.camera.AnimationManager;
40 import com.android.camera.ButtonManager;
41 import com.android.camera.CaptureLayoutHelper;
42 import com.android.camera.ShutterButton;
43 import com.android.camera.TextureViewHelper;
44 import com.android.camera.debug.Log;
45 import com.android.camera.filmstrip.FilmstripContentPanel;
46 import com.android.camera.hardware.HardwareSpec;
47 import com.android.camera.module.ModuleController;
48 import com.android.camera.settings.Keys;
49 import com.android.camera.settings.SettingsManager;
50 import com.android.camera.ui.AbstractTutorialOverlay;
51 import com.android.camera.ui.BottomBar;
52 import com.android.camera.ui.CaptureAnimationOverlay;
53 import com.android.camera.ui.GridLines;
54 import com.android.camera.ui.MainActivityLayout;
55 import com.android.camera.ui.MarginDrawable;
56 import com.android.camera.ui.ModeListView;
57 import com.android.camera.ui.ModeTransitionView;
58 import com.android.camera.ui.PreviewOverlay;
59 import com.android.camera.ui.PreviewStatusListener;
60 import com.android.camera.ui.StickyBottomCaptureLayout;
61 import com.android.camera.ui.TouchCoordinate;
62 import com.android.camera.ui.focus.FocusRing;
63 import com.android.camera.util.AndroidServices;
64 import com.android.camera.util.ApiHelper;
65 import com.android.camera.util.CameraUtil;
66 import com.android.camera.util.Gusterpolator;
67 import com.android.camera.util.PhotoSphereHelper;
68 import com.android.camera.widget.Cling;
69 import com.android.camera.widget.FilmstripLayout;
70 import com.android.camera.widget.IndicatorIconController;
71 import com.android.camera.widget.ModeOptionsOverlay;
72 import com.android.camera.widget.RoundedThumbnailView;
73 import com.android.camera2.R;
75 import java.util.List;
78 * CameraAppUI centralizes control of views shared across modules. Whereas module
79 * specific views will be handled in each Module UI. For example, we can now
80 * bring the flash animation and capture animation up from each module to app
81 * level, as these animations are largely the same for all modules.
83 * This class also serves to disambiguate touch events. It recognizes all the
84 * swipe gestures that happen on the preview by attaching a touch listener to
85 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
86 * of how swipe from each direction should be handled, it can then redirect these
87 * events to appropriate recipient views.
89 public class CameraAppUI implements ModeListView.ModeSwitchListener,
90 TextureView.SurfaceTextureListener,
91 ModeListView.ModeListOpenListener,
92 SettingsManager.OnSettingChangedListener,
93 ShutterButton.OnShutterButtonListener {
96 * The bottom controls on the filmstrip.
98 public static interface BottomPanel {
99 /** Values for the view state of the button. */
100 public final int VIEWER_NONE = 0;
101 public final int VIEWER_PHOTO_SPHERE = 1;
102 public final int VIEWER_REFOCUS = 2;
103 public final int VIEWER_OTHER = 3;
106 * Sets a new or replaces an existing listener for bottom control events.
108 void setListener(Listener listener);
111 * Sets cling for external viewer button.
113 void setClingForViewer(int viewerType, Cling cling);
116 * Clears cling for external viewer button.
118 void clearClingForViewer(int viewerType);
121 * Returns a cling for the specified viewer type.
123 Cling getClingForViewer(int viewerType);
126 * Set if the bottom controls are visible.
127 * @param visible {@code true} if visible.
129 void setVisible(boolean visible);
132 * @param visible Whether the button is visible.
134 void setEditButtonVisibility(boolean visible);
137 * @param enabled Whether the button is enabled.
139 void setEditEnabled(boolean enabled);
142 * Sets the visibility of the view-photosphere button.
144 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
145 * {@link #VIEWER_REFOCUS}.
147 void setViewerButtonVisibility(int state);
150 * @param enabled Whether the button is enabled.
152 void setViewEnabled(boolean enabled);
155 * @param enabled Whether the button is enabled.
157 void setTinyPlanetEnabled(boolean enabled);
160 * @param visible Whether the button is visible.
162 void setDeleteButtonVisibility(boolean visible);
165 * @param enabled Whether the button is enabled.
167 void setDeleteEnabled(boolean enabled);
170 * @param visible Whether the button is visible.
172 void setShareButtonVisibility(boolean visible);
175 * @param enabled Whether the button is enabled.
177 void setShareEnabled(boolean enabled);
180 * Sets the texts for progress UI.
182 * @param text The text to show.
184 void setProgressText(CharSequence text);
189 * @param progress The progress value. Should be between 0 and 100.
191 void setProgress(int progress);
194 * Replaces the progress UI with an error message.
196 void showProgressError(CharSequence message);
199 * Hide the progress error message.
201 void hideProgressError();
204 * Shows the progress.
209 * Hides the progress.
214 * Shows the controls.
219 * Hides the controls.
224 * Classes implementing this interface can listen for events on the bottom
227 public static interface Listener {
229 * Called when the user pressed the "view" button to e.g. view a photo
230 * sphere or RGBZ image.
232 public void onExternalViewer();
235 * Called when the "edit" button is pressed.
237 public void onEdit();
240 * Called when the "tiny planet" button is pressed.
242 public void onTinyPlanet();
245 * Called when the "delete" button is pressed.
247 public void onDelete();
250 * Called when the "share" button is pressed.
252 public void onShare();
255 * Called when the progress error message is clicked.
257 public void onProgressErrorClicked();
262 * BottomBarUISpec provides a structure for modules
263 * to specify their ideal bottom bar mode options layout.
265 * Once constructed by a module, this class should be
266 * treated as read only.
268 * The application then edits this spec according to
269 * hardware limitations and displays the final bottom
272 public static class BottomBarUISpec {
273 /** Mode options UI */
276 * Set true if the camera option should be enabled.
277 * If not set or false, and multiple cameras are supported,
278 * the camera option will be disabled.
280 * If multiple cameras are not supported, this preference
281 * is ignored and the camera option will not be visible.
283 public boolean enableCamera;
286 * Set true if the camera option should not be visible, regardless
287 * of hardware limitations.
289 public boolean hideCamera;
292 * Set true if the photo flash option should be enabled.
293 * If not set or false, the photo flash option will be
296 * If the hardware does not support multiple flash values,
297 * this preference is ignored and the flash option will
298 * be disabled. It will not be made invisible in order to
299 * preserve a consistent experience across devices and between
300 * front and back cameras.
302 public boolean enableFlash;
305 * Set true if the video flash option should be enabled.
306 * Same disable rules apply as the photo flash option.
308 public boolean enableTorchFlash;
311 * Set true if the HDR+ flash option should be enabled.
312 * Same disable rules apply as the photo flash option.
314 public boolean enableHdrPlusFlash;
317 * Set true if flash should not be visible, regardless of
318 * hardware limitations.
320 public boolean hideFlash;
323 * Set true if the hdr/hdr+ option should be enabled.
324 * If not set or false, the hdr/hdr+ option will be disabled.
326 * Hdr or hdr+ will be chosen based on hardware limitations,
327 * with hdr+ prefered.
329 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
330 * will not be visible.
332 public boolean enableHdr;
335 * Set true if hdr/hdr+ should not be visible, regardless of
336 * hardware limitations.
338 public boolean hideHdr;
341 * Set true if grid lines should be visible. Not setting this
342 * causes grid lines to be disabled. This option is agnostic to
345 public boolean enableGridLines;
348 * Set true if grid lines should not be visible.
350 public boolean hideGridLines;
353 * Set true if the panorama orientation option should be visible.
355 * This option is not constrained by hardware limitations.
357 public boolean enablePanoOrientation;
359 public boolean enableExposureCompensation;
364 * Set true if the intent ui cancel option should be visible.
366 public boolean showCancel;
368 * Set true if the intent ui done option should be visible.
370 public boolean showDone;
372 * Set true if the intent ui retake option should be visible.
374 public boolean showRetake;
376 * Set true if the intent ui review option should be visible.
378 public boolean showReview;
380 /** Mode options callbacks */
383 * A {@link com.android.camera.ButtonManager.ButtonCallback}
384 * that will be executed when the camera option is pressed. This
385 * callback can be null.
387 public ButtonManager.ButtonCallback cameraCallback;
390 * A {@link com.android.camera.ButtonManager.ButtonCallback}
391 * that will be executed when the flash option is pressed. This
392 * callback can be null.
394 public ButtonManager.ButtonCallback flashCallback;
397 * A {@link com.android.camera.ButtonManager.ButtonCallback}
398 * that will be executed when the hdr/hdr+ option is pressed. This
399 * callback can be null.
401 public ButtonManager.ButtonCallback hdrCallback;
404 * A {@link com.android.camera.ButtonManager.ButtonCallback}
405 * that will be executed when the grid lines option is pressed. This
406 * callback can be null.
408 public ButtonManager.ButtonCallback gridLinesCallback;
411 * A {@link com.android.camera.ButtonManager.ButtonCallback}
412 * that will execute when the panorama orientation option is pressed.
413 * This callback can be null.
415 public ButtonManager.ButtonCallback panoOrientationCallback;
417 /** Intent UI callbacks */
420 * A {@link android.view.View.OnClickListener} that will execute
421 * when the cancel option is pressed. This callback can be null.
423 public View.OnClickListener cancelCallback;
426 * A {@link android.view.View.OnClickListener} that will execute
427 * when the done option is pressed. This callback can be null.
429 public View.OnClickListener doneCallback;
432 * A {@link android.view.View.OnClickListener} that will execute
433 * when the retake option is pressed. This callback can be null.
435 public View.OnClickListener retakeCallback;
438 * A {@link android.view.View.OnClickListener} that will execute
439 * when the review option is pressed. This callback can be null.
441 public View.OnClickListener reviewCallback;
444 * A ExposureCompensationSetCallback that will execute
445 * when an expsosure button is pressed. This callback can be null.
447 public interface ExposureCompensationSetCallback {
448 public void setExposure(int value);
450 public ExposureCompensationSetCallback exposureCompensationSetCallback;
453 * Exposure compensation parameters.
455 public int minExposureCompensation;
456 public int maxExposureCompensation;
457 public float exposureCompensationStep;
460 * Whether self-timer is enabled.
462 public boolean enableSelfTimer = false;
465 * Whether the option for self-timer should show. If true and
466 * {@link #enableSelfTimer} is false, then the option should be shown
469 public boolean showSelfTimer = false;
473 private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
475 private final AppController mController;
476 private final boolean mIsCaptureIntent;
477 private final AnimationManager mAnimationManager;
480 private final static int IDLE = 0;
481 private final static int SWIPE_UP = 1;
482 private final static int SWIPE_DOWN = 2;
483 private final static int SWIPE_LEFT = 3;
484 private final static int SWIPE_RIGHT = 4;
485 private boolean mSwipeEnabled = true;
487 // Shared Surface Texture properities.
488 private SurfaceTexture mSurface;
489 private int mSurfaceWidth;
490 private int mSurfaceHeight;
492 // Touch related measures:
493 private final int mSlop;
494 private final static int SWIPE_TIME_OUT_MS = 500;
496 // Mode cover states:
497 private final static int COVER_HIDDEN = 0;
498 private final static int COVER_SHOWN = 1;
499 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
500 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
503 * Preview down-sample rate when taking a screenshot.
505 private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
508 private final FrameLayout mCameraRootView;
509 private final ModeTransitionView mModeTransitionView;
510 private final MainActivityLayout mAppRootView;
511 private final ModeListView mModeListView;
512 private final FilmstripLayout mFilmstripLayout;
513 private TextureView mTextureView;
514 private FrameLayout mModuleUI;
515 private ShutterButton mShutterButton;
516 private BottomBar mBottomBar;
517 private ModeOptionsOverlay mModeOptionsOverlay;
518 private IndicatorIconController mIndicatorIconController;
519 private FocusRing mFocusRing;
520 private FrameLayout mTutorialsPlaceHolderWrapper;
521 private StickyBottomCaptureLayout mStickyBottomCaptureLayout;
522 private TextureViewHelper mTextureViewHelper;
523 private final GestureDetector mGestureDetector;
524 private DisplayManager.DisplayListener mDisplayListener;
525 private int mLastRotation;
526 private int mSwipeState = IDLE;
527 private PreviewOverlay mPreviewOverlay;
528 private GridLines mGridLines;
529 private CaptureAnimationOverlay mCaptureOverlay;
530 private PreviewStatusListener mPreviewStatusListener;
531 private int mModeCoverState = COVER_HIDDEN;
532 private final FilmstripBottomPanel mFilmstripBottomControls;
533 private final FilmstripContentPanel mFilmstripPanel;
534 private Runnable mHideCoverRunnable;
535 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
536 = new View.OnLayoutChangeListener() {
538 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
539 int oldTop, int oldRight, int oldBottom) {
540 if (mPreviewStatusListener != null) {
541 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
542 oldTop, oldRight, oldBottom);
546 private View mModeOptionsToggle;
547 private final RoundedThumbnailView mRoundedThumbnailView;
548 private final CaptureLayoutHelper mCaptureLayoutHelper;
549 private boolean mAccessibilityEnabled;
550 private final View mAccessibilityAffordances;
552 private boolean mDisableAllUserInteractions;
553 /** Whether to prevent capture indicator from being triggered. */
554 private boolean mSuppressCaptureIndicator;
557 * Provides current preview frame and the controls/overlay from the module that
558 * are shown on top of the preview.
560 public interface CameraModuleScreenShotProvider {
562 * Returns the current preview frame down-sampled using the given down-sample
565 * @param downSampleFactor the down sample factor for down sampling the
566 * preview frame. (e.g. a down sample factor of
567 * 2 means to scale down the preview frame to 1/2
568 * the width and height.)
569 * @return down-sampled preview frame
571 public Bitmap getPreviewFrame(int downSampleFactor);
574 * @return the controls and overlays that are currently showing on top of
575 * the preview drawn into a bitmap with no scaling applied.
577 public Bitmap getPreviewOverlayAndControls();
580 * Returns a bitmap containing the current screenshot.
582 * @param previewDownSampleFactor the downsample factor applied on the
583 * preview frame when taking the screenshot
585 public Bitmap getScreenShot(int previewDownSampleFactor);
589 * This listener gets called when the size of the window (excluding the system
590 * decor such as status bar and nav bar) has changed.
592 public interface NonDecorWindowSizeChangedListener {
593 public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
596 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
597 new CameraModuleScreenShotProvider() {
599 public Bitmap getPreviewFrame(int downSampleFactor) {
600 if (mCameraRootView == null || mTextureView == null) {
603 // Gets the bitmap from the preview TextureView.
604 Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
609 public Bitmap getPreviewOverlayAndControls() {
610 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
611 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
612 Canvas canvas = new Canvas(overlays);
613 mCameraRootView.draw(canvas);
618 public Bitmap getScreenShot(int previewDownSampleFactor) {
619 Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
620 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
621 Canvas canvas = new Canvas(screenshot);
622 canvas.drawARGB(255, 0, 0, 0);
623 Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
624 if (preview != null) {
625 canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
627 Bitmap overlay = getPreviewOverlayAndControls();
628 if (overlay != null) {
629 canvas.drawBitmap(overlay, 0f, 0f, null);
635 private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
637 public long getCoverHiddenTime() {
638 return mCoverHiddenTime;
642 * This resets the preview to have no applied transform matrix.
644 public void clearPreviewTransform() {
645 mTextureViewHelper.clearTransform();
648 public void updatePreviewAspectRatio(float aspectRatio) {
649 mTextureViewHelper.updateAspectRatio(aspectRatio);
653 * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
654 * its TextureView. This is necessary to get the expected behavior for the TextureView's
655 * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
656 * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
659 * b/17286155 - Tracking a fix for this in HardwareLayer.
661 public void setDefaultBufferSizeToViewDimens() {
662 if (mSurface == null || mTextureView == null) {
663 Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
666 mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
670 * Updates the preview matrix without altering it.
673 * @param aspectRatio the desired aspect ratio for the preview.
675 public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
676 mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
680 * @return the rect that will display the preview.
682 public RectF getFullscreenRect() {
683 return mTextureViewHelper.getFullscreenRect();
687 * This is to support modules that calculate their own transform matrix because
688 * they need to use a transform matrix to rotate the preview.
690 * @param matrix transform matrix to be set on the TextureView
692 public void updatePreviewTransform(Matrix matrix) {
693 mTextureViewHelper.updateTransform(matrix);
696 public interface AnimationFinishedListener {
697 public void onAnimationFinished(boolean success);
700 private class MyTouchListener implements View.OnTouchListener {
701 private boolean mScaleStarted = false;
703 public boolean onTouch(View v, MotionEvent event) {
704 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
705 mScaleStarted = false;
706 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
707 mScaleStarted = true;
709 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
714 * This gesture listener finds out the direction of the scroll gestures and
715 * sends them to CameraAppUI to do further handling.
717 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
718 private MotionEvent mDown;
721 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
722 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
723 || mSwipeState != IDLE
729 int deltaX = (int) (ev.getX() - mDown.getX());
730 int deltaY = (int) (ev.getY() - mDown.getY());
731 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
732 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
733 // Calculate the direction of the swipe.
734 if (deltaX >= Math.abs(deltaY)) {
736 setSwipeState(SWIPE_RIGHT);
737 } else if (deltaX <= -Math.abs(deltaY)) {
739 setSwipeState(SWIPE_LEFT);
746 private void setSwipeState(int swipeState) {
747 mSwipeState = swipeState;
748 // Notify new swipe detected.
749 onSwipeDetected(swipeState);
753 public boolean onDown(MotionEvent ev) {
754 mDown = MotionEvent.obtain(ev);
760 public CameraAppUI(AppController controller, MainActivityLayout appRootView,
761 boolean isCaptureIntent) {
762 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
763 mController = controller;
764 mIsCaptureIntent = isCaptureIntent;
766 mAppRootView = appRootView;
767 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
768 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
769 mModeTransitionView = (ModeTransitionView)
770 mAppRootView.findViewById(R.id.mode_transition_view);
771 mFilmstripBottomControls = new FilmstripBottomPanel(controller,
772 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
773 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
774 mGestureDetector = new GestureDetector(controller.getAndroidContext(),
775 new MyGestureListener());
776 Resources res = controller.getAndroidContext().getResources();
777 mCaptureLayoutHelper = new CaptureLayoutHelper(
778 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
779 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
780 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
781 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
782 if (mModeListView != null) {
783 mModeListView.setModeSwitchListener(this);
784 mModeListView.setModeListOpenListener(this);
785 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
786 mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
787 boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
788 SettingsManager.SCOPE_GLOBAL,
789 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
790 mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
792 Log.e(TAG, "Cannot find mode list in the view hierarchy");
794 mAnimationManager = new AnimationManager();
795 mRoundedThumbnailView = (RoundedThumbnailView) appRootView.findViewById(R.id.rounded_thumbnail_view);
796 mRoundedThumbnailView.setCallback(new RoundedThumbnailView.Callback() {
798 public void onHitStateFinished() {
799 mFilmstripLayout.showFilmstrip();
803 mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
804 initDisplayListener();
805 mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
806 View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
807 modeListToggle.setOnClickListener(new View.OnClickListener() {
809 public void onClick(View view) {
813 View filmstripToggle = mAppRootView.findViewById(
814 R.id.accessibility_filmstrip_toggle_button);
815 filmstripToggle.setOnClickListener(new View.OnClickListener() {
817 public void onClick(View view) {
822 mSuppressCaptureIndicator = false;
827 * Freeze what is currently shown on screen until the next preview frame comes
830 public void freezeScreenUntilPreviewReady() {
831 Log.v(TAG, "freezeScreenUntilPreviewReady");
832 mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
833 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
834 mHideCoverRunnable = new Runnable() {
837 mModeTransitionView.hideImageCover();
840 mModeCoverState = COVER_SHOWN;
844 * Creates a cling for the specific viewer and links the cling to the corresponding
845 * button for layout position.
847 * @param viewerType defines which viewer the cling is for.
849 public void setupClingForViewer(int viewerType) {
850 if (viewerType == BottomPanel.VIEWER_REFOCUS) {
851 FrameLayout filmstripContent = (FrameLayout) mAppRootView
852 .findViewById(R.id.camera_filmstrip_content_layout);
853 if (filmstripContent != null) {
854 // Creates refocus cling.
855 LayoutInflater inflater = AndroidServices.instance().provideLayoutInflater();
856 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
857 // Sets instruction text in the cling.
858 refocusCling.setText(mController.getAndroidContext().getResources()
859 .getString(R.string.cling_text_for_refocus_editor_button));
861 // Adds cling into view hierarchy.
862 int clingWidth = mController.getAndroidContext()
863 .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
864 filmstripContent.addView(refocusCling, clingWidth,
865 ViewGroup.LayoutParams.WRAP_CONTENT);
866 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
872 * Clears the listeners for the cling and remove it from the view hierarchy.
874 * @param viewerType defines which viewer the cling is for.
876 public void clearClingForViewer(int viewerType) {
877 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
878 if (clingToBeRemoved == null) {
879 // No cling is created for the specific viewer type.
882 mFilmstripBottomControls.clearClingForViewer(viewerType);
883 clingToBeRemoved.setVisibility(View.GONE);
884 mAppRootView.removeView(clingToBeRemoved);
888 * Enable or disable swipe gestures. We want to disable them e.g. while we
891 public void setSwipeEnabled(boolean enabled) {
892 mSwipeEnabled = enabled;
893 // TODO: This can be removed once we come up with a new design for handling swipe
894 // on shutter button and mode options. (More details: b/13751653)
895 mAppRootView.setSwipeEnabled(enabled);
898 public void onDestroy() {
899 AndroidServices.instance().provideDisplayManager()
900 .unregisterDisplayListener(mDisplayListener);
904 * Initializes the display listener to listen to display changes such as
905 * 180-degree rotation change, which will not have an onConfigurationChanged
908 private void initDisplayListener() {
909 if (ApiHelper.HAS_DISPLAY_LISTENER) {
910 mLastRotation = CameraUtil.getDisplayRotation();
912 mDisplayListener = new DisplayManager.DisplayListener() {
914 public void onDisplayAdded(int arg0) {
919 public void onDisplayChanged(int displayId) {
920 int rotation = CameraUtil.getDisplayRotation(
922 if ((rotation - mLastRotation + 360) % 360 == 180
923 && mPreviewStatusListener != null) {
924 mPreviewStatusListener.onPreviewFlipped();
925 mStickyBottomCaptureLayout.requestLayout();
926 mModeListView.requestLayout();
927 mTextureView.requestLayout();
929 mLastRotation = rotation;
933 public void onDisplayRemoved(int arg0) {
938 AndroidServices.instance().provideDisplayManager()
939 .registerDisplayListener(mDisplayListener, null);
944 * Redirects touch events to appropriate recipient views based on swipe direction.
945 * More specifically, swipe up and swipe down will be handled by the view that handles
946 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
947 * to mode list in order to bring up mode list.
949 private void onSwipeDetected(int swipeState) {
950 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
951 // TODO: Polish quick switch after this release.
952 // Quick switch between modes.
953 int currentModuleIndex = mController.getCurrentModuleIndex();
954 final int moduleToTransitionTo =
955 mController.getQuickSwitchToModuleId(currentModuleIndex);
956 if (currentModuleIndex != moduleToTransitionTo) {
957 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
958 int shadeColorId = R.color.camera_gray_background;
959 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
960 mController.getAndroidContext());
962 AnimationFinishedListener listener = new AnimationFinishedListener() {
964 public void onAnimationFinished(boolean success) {
966 mHideCoverRunnable = new Runnable() {
969 mModeTransitionView.startPeepHoleAnimation();
972 mModeCoverState = COVER_SHOWN;
973 // Go to new module when the previous operation is successful.
974 mController.onModeSelected(moduleToTransitionTo);
979 } else if (swipeState == SWIPE_LEFT) {
980 // Pass the touch sequence to filmstrip layout.
981 mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
982 } else if (swipeState == SWIPE_RIGHT) {
983 // Pass the touch to mode switcher
984 mAppRootView.redirectTouchEventsTo(mModeListView);
989 * Gets called when activity resumes in preview.
991 public void resume() {
992 // Show mode theme cover until preview is ready
993 showModeCoverUntilPreviewReady();
995 // Hide action bar first since we are in full screen mode first, and
996 // switch the system UI to lights-out mode.
997 mFilmstripPanel.hide();
999 // Show UI that is meant to only be used when spoken feedback is
1001 mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
1002 mAccessibilityAffordances.setVisibility(
1003 (!mIsCaptureIntent && mAccessibilityEnabled) ? View.VISIBLE : View.GONE);
1007 * @return Whether any spoken feedback accessibility feature is currently
1010 private boolean isSpokenFeedbackAccessibilityEnabled() {
1011 AccessibilityManager accessibilityManager = AndroidServices.instance()
1012 .provideAccessibilityManager();
1013 List<AccessibilityServiceInfo> infos = accessibilityManager
1014 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1015 return infos != null && !infos.isEmpty();
1019 * Opens the mode list (e.g. because of the menu button being pressed) and
1020 * adapts the rest of the UI.
1022 public void openModeList() {
1023 mModeOptionsOverlay.closeModeOptions();
1024 mModeListView.onMenuPressed();
1028 * A cover view showing the mode theme color and mode icon will be visible on
1029 * top of preview until preview is ready (i.e. camera preview is started and
1030 * the first frame has been received).
1032 private void showModeCoverUntilPreviewReady() {
1033 int modeId = mController.getCurrentModuleIndex();
1034 int colorId = R.color.camera_gray_background;;
1035 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1036 mModeTransitionView.setupModeCover(colorId, iconId);
1037 mHideCoverRunnable = new Runnable() {
1040 mModeTransitionView.hideModeCover(null);
1041 if (!mDisableAllUserInteractions) {
1042 showShimmyDelayed();
1046 mModeCoverState = COVER_SHOWN;
1049 private void showShimmyDelayed() {
1050 if (!mIsCaptureIntent) {
1051 // Show shimmy in SHIMMY_DELAY_MS
1052 mModeListView.showModeSwitcherHint();
1056 private void hideModeCover() {
1057 if (mHideCoverRunnable != null) {
1058 mAppRootView.post(mHideCoverRunnable);
1059 mHideCoverRunnable = null;
1061 mModeCoverState = COVER_HIDDEN;
1062 if (mCoverHiddenTime < 0) {
1063 mCoverHiddenTime = System.currentTimeMillis();
1068 public void onPreviewVisiblityChanged(int visibility) {
1069 if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1070 setIndicatorBottomBarWrapperVisible(false);
1071 mAccessibilityAffordances.setVisibility(View.GONE);
1073 setIndicatorBottomBarWrapperVisible(true);
1074 if (!mIsCaptureIntent && mAccessibilityEnabled) {
1075 mAccessibilityAffordances.setVisibility(View.VISIBLE);
1077 mAccessibilityAffordances.setVisibility(View.GONE);
1083 * Call to stop the preview from being rendered. Sets the entire capture
1084 * root view to invisible which includes the preview plus focus indicator
1085 * and any other auxiliary views for capture modes.
1087 public void pausePreviewRendering() {
1088 mCameraRootView.setVisibility(View.INVISIBLE);
1092 * Call to begin rendering the preview and auxiliary views again.
1094 public void resumePreviewRendering() {
1095 mCameraRootView.setVisibility(View.VISIBLE);
1099 * Returns the transform associated with the preview view.
1101 * @param m the Matrix in which to copy the current transform.
1102 * @return The specified matrix if not null or a new Matrix instance
1105 public Matrix getPreviewTransform(Matrix m) {
1106 return mTextureView.getTransform(m);
1110 public void onOpenFullScreen() {
1115 public void onModeListOpenProgress(float progress) {
1116 progress = 1 - progress;
1117 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1118 mModeOptionsToggle.setAlpha(interpolatedProgress);
1119 // Change shutter button alpha linearly based on the mode list open progress:
1120 // set the alpha to disabled alpha when list is fully open, to enabled alpha
1121 // when the list is fully closed.
1122 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1123 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1127 public void onModeListClosed() {
1128 // Make sure the alpha on mode options ellipse is reset when mode drawer
1130 mModeOptionsToggle.setAlpha(1f);
1131 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1135 * Called when the back key is pressed.
1137 * @return Whether the UI responded to the key event.
1139 public boolean onBackPressed() {
1140 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1141 return mFilmstripLayout.onBackPressed();
1143 return mModeListView.onBackPressed();
1148 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1149 * listens to SurfaceTexture changes. In addition, listeners are set on
1150 * dependent app ui elements.
1152 * @param previewStatusListener the listener that gets notified when SurfaceTexture
1155 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1156 mPreviewStatusListener = previewStatusListener;
1157 if (mPreviewStatusListener != null) {
1158 onPreviewListenerChanged();
1163 * When the PreviewStatusListener changes, listeners need to be
1164 * set on the following app ui elements:
1165 * {@link com.android.camera.ui.PreviewOverlay},
1166 * {@link com.android.camera.ui.BottomBar},
1167 * {@link com.android.camera.ui.IndicatorIconController}.
1169 private void onPreviewListenerChanged() {
1170 // Set a listener for recognizing preview gestures.
1171 GestureDetector.OnGestureListener gestureListener
1172 = mPreviewStatusListener.getGestureListener();
1173 if (gestureListener != null) {
1174 mPreviewOverlay.setGestureListener(gestureListener);
1176 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1177 if (touchListener != null) {
1178 mPreviewOverlay.setTouchListener(touchListener);
1181 mTextureViewHelper.setAutoAdjustTransform(
1182 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1186 * This method should be called in onCameraOpened. It defines CameraAppUI
1187 * specific changes that depend on the camera or camera settings.
1189 public void onChangeCamera() {
1190 ModuleController moduleController = mController.getCurrentModuleController();
1191 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1193 if (mIndicatorIconController != null) {
1194 // Sync the settings state with the indicator state.
1195 mIndicatorIconController.syncIndicators();
1200 * Adds a listener to receive callbacks when preview area changes.
1202 public void addPreviewAreaChangedListener(
1203 PreviewStatusListener.PreviewAreaChangedListener listener) {
1204 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1208 * Removes a listener that receives callbacks when preview area changes.
1210 public void removePreviewAreaChangedListener(
1211 PreviewStatusListener.PreviewAreaChangedListener listener) {
1212 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1216 * This inflates generic_module layout, which contains all the shared views across
1217 * modules. Then each module inflates their own views in the given view group. For
1218 * now, this is called every time switching from a not-yet-refactored module to a
1219 * refactored module. In the future, this should only need to be done once per app
1222 public void prepareModuleUI() {
1223 mController.getSettingsManager().addListener(this);
1224 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1225 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1226 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1227 mController.getCameraProvider());
1228 mTextureViewHelper.setSurfaceTextureListener(this);
1229 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1231 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1232 int unpressedColor = mController.getAndroidContext().getResources()
1233 .getColor(R.color.camera_gray_background);
1234 setBottomBarColor(unpressedColor);
1235 updateModeSpecificUIColors();
1237 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1240 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1242 // Sets the visibility of the bottom bar and the mode options.
1243 resetBottomControls(mController.getCurrentModuleController(),
1244 mController.getCurrentModuleIndex());
1245 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1247 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1248 addShutterListener(mController.getCurrentModuleController());
1249 addShutterListener(mModeOptionsOverlay);
1250 addShutterListener(this);
1252 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1253 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1255 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1256 mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1257 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1259 mCaptureOverlay = (CaptureAnimationOverlay)
1260 mCameraRootView.findViewById(R.id.capture_overlay);
1261 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1262 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1264 if (mIndicatorIconController == null) {
1265 mIndicatorIconController =
1266 new IndicatorIconController(mController, mAppRootView);
1269 mController.getButtonManager().load(mCameraRootView);
1270 mController.getButtonManager().setListener(mIndicatorIconController);
1271 mController.getSettingsManager().addListener(mIndicatorIconController);
1273 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1274 mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1275 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1276 .findViewById(R.id.tutorials_placeholder_wrapper);
1277 mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1278 .findViewById(R.id.sticky_bottom_capture_layout);
1279 mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1281 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1282 mTextureViewHelper.addAspectRatioChangedListener(
1283 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1285 public void onPreviewAspectRatioChanged(float aspectRatio) {
1286 mModeOptionsOverlay.requestLayout();
1287 mBottomBar.requestLayout();
1294 * Called indirectly from each module in their initialization to get a view group
1295 * to inflate the module specific views in.
1297 * @return a view group for modules to attach views to
1299 public FrameLayout getModuleRootView() {
1300 // TODO: Change it to mModuleUI when refactor is done
1301 return mCameraRootView;
1305 * Remove all the module specific views.
1307 public void clearModuleUI() {
1308 if (mModuleUI != null) {
1309 mModuleUI.removeAllViews();
1311 removeShutterListener(mController.getCurrentModuleController());
1312 mTutorialsPlaceHolderWrapper.removeAllViews();
1313 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1315 setShutterButtonEnabled(true);
1316 mPreviewStatusListener = null;
1317 mPreviewOverlay.reset();
1319 Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1320 mFocusRing.stopFocusAnimations();
1324 * Gets called when preview is ready to start. It sets up one shot preview callback
1325 * in order to receive a callback when the preview frame is available, so that
1326 * the preview cover can be hidden to reveal preview.
1328 * An alternative for getting the timing to hide preview cover is through
1329 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1330 * which is less accurate but therefore is the fallback for modules that manage
1331 * their own preview callbacks (as setting one preview callback will override
1332 * any other installed preview callbacks), or use camera2 API.
1334 public void onPreviewReadyToStart() {
1335 if (mModeCoverState == COVER_SHOWN) {
1336 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1337 mController.setupOneShotPreviewListener();
1342 * Gets called when preview is started.
1344 public void onPreviewStarted() {
1345 Log.v(TAG, "onPreviewStarted");
1346 if (mModeCoverState == COVER_SHOWN) {
1347 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1349 enableModeOptions();
1353 * Gets notified when next preview frame comes in.
1355 public void onNewPreviewFrame() {
1356 Log.v(TAG, "onNewPreviewFrame");
1357 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1362 public void onShutterButtonClick() {
1364 * Set the mode options toggle unclickable, generally
1365 * throughout the app, whenever the shutter button is clicked.
1367 * This could be done in the OnShutterButtonListener of the
1368 * ModeOptionsOverlay, but since it is very important that we
1369 * can clearly see when the toggle becomes clickable again,
1370 * keep all of that logic at this level.
1372 // disableModeOptions();
1376 public void onShutterCoordinate(TouchCoordinate coord) {
1381 public void onShutterButtonFocus(boolean pressed) {
1386 public void onShutterButtonLongPressed() {
1391 * Set the mode options toggle clickable.
1393 public void enableModeOptions() {
1395 * For modules using camera 1 api, this gets called in
1396 * onSurfaceTextureUpdated whenever the preview gets stopped and
1397 * started after each capture. This also takes care of the
1398 * case where the mode options might be unclickable when we
1401 * For modules using camera 2 api, they're required to call this
1402 * method when a capture is "completed". Unfortunately this differs
1403 * per module implementation.
1405 if (!mDisableAllUserInteractions) {
1406 mModeOptionsOverlay.setToggleClickable(true);
1411 * Set the mode options toggle not clickable.
1413 public void disableModeOptions() {
1414 mModeOptionsOverlay.setToggleClickable(false);
1417 public void setDisableAllUserInteractions(boolean disable) {
1419 disableModeOptions();
1420 setShutterButtonEnabled(false);
1421 setSwipeEnabled(false);
1422 mModeListView.hideAnimated();
1424 enableModeOptions();
1425 setShutterButtonEnabled(true);
1426 setSwipeEnabled(true);
1428 mDisableAllUserInteractions = disable;
1432 public void onModeButtonPressed(int modeIndex) {
1433 // TODO: Make CameraActivity listen to ModeListView's events.
1434 int pressedModuleId = mController.getModuleId(modeIndex);
1435 int currentModuleId = mController.getCurrentModuleIndex();
1436 if (pressedModuleId != currentModuleId) {
1437 hideCaptureIndicator();
1442 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1444 * @param modeIndex mode index of the selected mode
1447 public void onModeSelected(int modeIndex) {
1448 mHideCoverRunnable = new Runnable() {
1451 mModeListView.startModeSelectionAnimation();
1454 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1455 mModeCoverState = COVER_SHOWN;
1457 int lastIndex = mController.getCurrentModuleIndex();
1458 // Actual mode teardown / new mode initialization happens here
1459 mController.onModeSelected(modeIndex);
1460 int currentIndex = mController.getCurrentModuleIndex();
1462 if (lastIndex == currentIndex) {
1466 updateModeSpecificUIColors();
1469 private void updateModeSpecificUIColors() {
1470 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1474 public void onSettingsSelected() {
1475 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1476 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1477 mModeListView.setShouldShowSettingsCling(false);
1478 mController.onSettingsSelected();
1482 public int getCurrentModeIndex() {
1483 return mController.getCurrentModuleIndex();
1486 /********************** Capture animation **********************/
1487 /* TODO: This session is subject to UX changes. In addition to the generic
1488 flash animation and post capture animation, consider designating a parameter
1489 for specifying the type of animation, as well as an animation finished listener
1490 so that modules can have more knowledge of the status of the animation. */
1493 * Turns on or off the capture indicator suppression.
1495 public void setShouldSuppressCaptureIndicator(boolean suppress) {
1496 mSuppressCaptureIndicator = suppress;
1500 * Starts the capture indicator pop-out animation.
1502 * @param accessibilityString An accessibility String to be announced during the peek animation.
1504 public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1505 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
1508 mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1512 * Updates the thumbnail image in the capture indicator.
1514 * @param thumbnailBitmap The thumbnail image to be shown.
1516 public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
1517 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
1520 mRoundedThumbnailView.setThumbnail(thumbnailBitmap);
1521 mRoundedThumbnailView.setRotation(rotation);
1525 * Hides the capture indicator.
1527 public void hideCaptureIndicator() {
1528 mRoundedThumbnailView.hideThumbnail();
1532 * Starts the flash animation.
1534 public void startFlashAnimation(boolean shortFlash) {
1535 mCaptureOverlay.startFlashAnimation(shortFlash);
1539 * Cancels the pre-capture animation.
1541 public void cancelPreCaptureAnimation() {
1542 mAnimationManager.cancelAnimations();
1546 * Cancels the post-capture animation.
1548 public void cancelPostCaptureAnimation() {
1549 mAnimationManager.cancelAnimations();
1552 public FilmstripContentPanel getFilmstripContentPanel() {
1553 return mFilmstripPanel;
1557 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1558 * bottom of the filmstrip.
1560 public BottomPanel getFilmstripBottomControls() {
1561 return mFilmstripBottomControls;
1564 public void showBottomControls() {
1565 mFilmstripBottomControls.show();
1568 public void hideBottomControls() {
1569 mFilmstripBottomControls.hide();
1573 * @param listener The listener for bottom controls.
1575 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1576 mFilmstripBottomControls.setListener(listener);
1579 /***************************SurfaceTexture Api and Listener*********************************/
1582 * Return the shared surface texture.
1584 public SurfaceTexture getSurfaceTexture() {
1589 * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1591 public int getSurfaceWidth() {
1592 return mSurfaceWidth;
1596 * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1598 public int getSurfaceHeight() {
1599 return mSurfaceHeight;
1603 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1605 mSurfaceWidth = width;
1606 mSurfaceHeight = height;
1607 Log.v(TAG, "SurfaceTexture is available");
1608 if (mPreviewStatusListener != null) {
1609 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1611 enableModeOptions();
1615 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1617 mSurfaceWidth = width;
1618 mSurfaceHeight = height;
1619 if (mPreviewStatusListener != null) {
1620 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1625 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1627 Log.v(TAG, "SurfaceTexture is destroyed");
1628 if (mPreviewStatusListener != null) {
1629 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1635 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1637 if (mPreviewStatusListener != null) {
1638 mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1640 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1641 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1642 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1647 /****************************Grid lines api ******************************/
1650 * Show a set of evenly spaced lines over the preview. The number
1651 * of lines horizontally and vertically is determined by
1652 * {@link com.android.camera.ui.GridLines}.
1654 public void showGridLines() {
1655 if (mGridLines != null) {
1656 mGridLines.setVisibility(View.VISIBLE);
1661 * Hide the set of evenly spaced grid lines overlaying the preview.
1663 public void hideGridLines() {
1664 if (mGridLines != null) {
1665 mGridLines.setVisibility(View.INVISIBLE);
1670 * Return a callback which shows or hide the preview grid lines
1671 * depending on whether the grid lines setting is set on.
1673 public ButtonManager.ButtonCallback getGridLinesCallback() {
1674 return new ButtonManager.ButtonCallback() {
1676 public void onStateChanged(int state) {
1677 if (!mController.isPaused()) {
1678 if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1688 /***************************Mode options api *****************************/
1691 * Set the mode options visible.
1693 public void showModeOptions() {
1694 /* Make mode options clickable. */
1695 enableModeOptions();
1696 mModeOptionsOverlay.setVisibility(View.VISIBLE);
1700 * Set the mode options invisible. This is necessary for modes
1701 * that don't show a bottom bar for the capture UI.
1703 public void hideModeOptions() {
1704 mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1707 /****************************Bottom bar api ******************************/
1710 * Sets up the bottom bar and mode options with the correct
1711 * shutter button and visibility based on the current module.
1713 public void resetBottomControls(ModuleController module, int moduleIndex) {
1714 if (areBottomControlsUsed(module)) {
1715 setBottomBarShutterIcon(moduleIndex);
1716 mCaptureLayoutHelper.setShowBottomBar(true);
1718 mCaptureLayoutHelper.setShowBottomBar(false);
1723 * Show or hide the mode options and bottom bar, based on
1724 * whether the current module is using the bottom bar. Returns
1725 * whether the mode options and bottom bar are used.
1727 private boolean areBottomControlsUsed(ModuleController module) {
1728 if (module.isUsingBottomBar()) {
1740 * Set the bottom bar visible.
1742 public void showBottomBar() {
1743 mBottomBar.setVisibility(View.VISIBLE);
1747 * Set the bottom bar invisible.
1749 public void hideBottomBar() {
1750 mBottomBar.setVisibility(View.INVISIBLE);
1754 * Sets the color of the bottom bar.
1756 public void setBottomBarColor(int colorId) {
1757 mBottomBar.setBackgroundColor(colorId);
1761 * Sets the pressed color of the bottom bar for a camera mode index.
1763 public void setBottomBarColorsForModeIndex(int index) {
1764 mBottomBar.setColorsForModeIndex(index);
1768 * Sets the shutter button icon on the bottom bar, based on
1771 public void setBottomBarShutterIcon(int modeIndex) {
1772 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1773 mController.getAndroidContext());
1774 mBottomBar.setShutterButtonIcon(shutterIconId);
1777 public void animateBottomBarToVideoStop(int shutterIconId) {
1778 mBottomBar.animateToVideoStop(shutterIconId);
1781 public void animateBottomBarToFullSize(int shutterIconId) {
1782 mBottomBar.animateToFullSize(shutterIconId);
1785 public void setShutterButtonEnabled(final boolean enabled) {
1786 if (!mDisableAllUserInteractions) {
1787 mBottomBar.post(new Runnable() {
1790 mBottomBar.setShutterButtonEnabled(enabled);
1796 public void setShutterButtonImportantToA11y(boolean important) {
1797 mBottomBar.setShutterButtonImportantToA11y(important);
1800 public boolean isShutterButtonEnabled() {
1801 return mBottomBar.isShutterButtonEnabled();
1804 public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1805 mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1809 * Set the visibility of the bottom bar.
1811 // TODO: needed for when panorama is managed by the generic module ui.
1812 public void setBottomBarVisible(boolean visible) {
1813 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1817 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1819 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1820 mShutterButton.addOnShutterButtonListener(listener);
1824 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1826 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1827 mShutterButton.removeOnShutterButtonListener(listener);
1831 * Performs a transition to the capture layout of the bottom bar.
1833 public void transitionToCapture() {
1834 ModuleController moduleController = mController.getCurrentModuleController();
1835 applyModuleSpecs(moduleController.getHardwareSpec(),
1836 moduleController.getBottomBarSpec());
1837 mBottomBar.transitionToCapture();
1842 * Displays the Cancel button instead of the capture button.
1844 public void transitionToCancel() {
1845 ModuleController moduleController = mController.getCurrentModuleController();
1846 applyModuleSpecs(moduleController.getHardwareSpec(),
1847 moduleController.getBottomBarSpec());
1848 mBottomBar.transitionToCancel();
1853 * Performs a transition to the global intent layout.
1855 public void transitionToIntentCaptureLayout() {
1856 ModuleController moduleController = mController.getCurrentModuleController();
1857 applyModuleSpecs(moduleController.getHardwareSpec(),
1858 moduleController.getBottomBarSpec());
1859 mBottomBar.transitionToIntentCaptureLayout();
1864 * Performs a transition to the global intent review layout.
1866 public void transitionToIntentReviewLayout() {
1867 ModuleController moduleController = mController.getCurrentModuleController();
1868 applyModuleSpecs(moduleController.getHardwareSpec(),
1869 moduleController.getBottomBarSpec());
1870 mBottomBar.transitionToIntentReviewLayout();
1875 * @return whether UI is in intent review mode
1877 public boolean isInIntentReview() {
1878 return mBottomBar.isInIntentReview();
1882 public void onSettingChanged(SettingsManager settingsManager, String key) {
1883 // Update the mode options based on the hardware spec,
1884 // when hdr changes to prevent flash from getting out of sync.
1885 if (key.equals(Keys.KEY_CAMERA_HDR)) {
1886 ModuleController moduleController = mController.getCurrentModuleController();
1887 applyModuleSpecs(moduleController.getHardwareSpec(),
1888 moduleController.getBottomBarSpec());
1893 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1894 * to the bottom bar mode options based on limitations from a
1895 * {@link com.android.camera.hardware.HardwareSpec}.
1897 * Options not supported by the hardware are either hidden
1898 * or disabled, depending on the option.
1900 * Otherwise, the option is fully enabled and clickable.
1902 public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1903 final BottomBarUISpec bottomBarSpec) {
1904 if (hardwareSpec == null || bottomBarSpec == null) {
1908 ButtonManager buttonManager = mController.getButtonManager();
1909 SettingsManager settingsManager = mController.getSettingsManager();
1911 buttonManager.setToInitialState();
1913 /** Standard mode options */
1914 if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1915 hardwareSpec.isFrontCameraSupported()) {
1916 if (bottomBarSpec.enableCamera) {
1917 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1918 bottomBarSpec.cameraCallback);
1920 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1923 // Hide camera icon if front camera not available.
1924 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1927 if (bottomBarSpec.hideFlash) {
1928 // Hide both flash and torch button in flash disable logic
1929 buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1930 buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1932 if (hardwareSpec.isFlashSupported()) {
1933 if (bottomBarSpec.enableFlash) {
1934 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1935 bottomBarSpec.flashCallback);
1936 } else if (bottomBarSpec.enableTorchFlash) {
1937 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1938 bottomBarSpec.flashCallback);
1939 } else if (bottomBarSpec.enableHdrPlusFlash) {
1940 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1941 bottomBarSpec.flashCallback);
1943 // Hide both flash and torch button in flash disable logic
1944 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1945 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1948 // Disable both flash and torch icon if not supported
1949 // by the chosen camera hardware.
1950 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1951 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1955 if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1956 // Force hide hdr or hdr plus icon.
1957 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1959 if (hardwareSpec.isHdrPlusSupported()) {
1960 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1961 mController.getModuleScope())) {
1962 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1963 bottomBarSpec.hdrCallback);
1965 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1967 } else if (hardwareSpec.isHdrSupported()) {
1968 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1969 mController.getModuleScope())) {
1970 buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1971 bottomBarSpec.hdrCallback);
1973 buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1976 // Hide hdr plus or hdr icon if neither are supported.
1977 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1981 if (bottomBarSpec.hideGridLines) {
1982 // Force hide grid lines icon.
1983 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1986 if (bottomBarSpec.enableGridLines) {
1987 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1988 bottomBarSpec.gridLinesCallback != null ?
1989 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1992 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1997 if (bottomBarSpec.enableSelfTimer) {
1998 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
2000 if (bottomBarSpec.showSelfTimer) {
2001 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
2003 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
2007 if (bottomBarSpec.enablePanoOrientation
2008 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
2009 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
2012 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
2013 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
2014 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
2015 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
2016 if (enableExposureCompensation) {
2017 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
2018 buttonManager.setExposureCompensationParameters(
2019 bottomBarSpec.minExposureCompensation,
2020 bottomBarSpec.maxExposureCompensation,
2021 bottomBarSpec.exposureCompensationStep);
2023 buttonManager.setExposureCompensationCallback(
2024 bottomBarSpec.exposureCompensationSetCallback);
2025 buttonManager.updateExposureButtons();
2027 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2028 buttonManager.setExposureCompensationCallback(null);
2032 if (bottomBarSpec.showCancel) {
2033 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2034 bottomBarSpec.cancelCallback);
2036 if (bottomBarSpec.showDone) {
2037 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2038 bottomBarSpec.doneCallback);
2040 if (bottomBarSpec.showRetake) {
2041 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2042 bottomBarSpec.retakeCallback,
2044 R.string.retake_button_description);
2046 if (bottomBarSpec.showReview) {
2047 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2048 bottomBarSpec.reviewCallback,
2050 R.string.review_button_description);
2055 * Shows the given tutorial on the screen.
2057 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2058 tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2061 /***************************Filmstrip api *****************************/
2063 public void showFilmstrip() {
2064 mModeListView.onBackPressed();
2065 mFilmstripLayout.showFilmstrip();
2068 public void hideFilmstrip() {
2069 mFilmstripLayout.hideFilmstrip();