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;
38 import android.widget.ImageButton;
40 import com.android.camera.AnimationManager;
41 import com.android.camera.ButtonManager;
42 import com.android.camera.CaptureLayoutHelper;
43 import com.android.camera.ShutterButton;
44 import com.android.camera.TextureViewHelper;
45 import com.android.camera.debug.Log;
46 import com.android.camera.filmstrip.FilmstripContentPanel;
47 import com.android.camera.hardware.HardwareSpec;
48 import com.android.camera.module.ModuleController;
49 import com.android.camera.settings.Keys;
50 import com.android.camera.settings.SettingsManager;
51 import com.android.camera.ui.AbstractTutorialOverlay;
52 import com.android.camera.ui.BottomBar;
53 import com.android.camera.ui.CaptureAnimationOverlay;
54 import com.android.camera.ui.GridLines;
55 import com.android.camera.ui.MainActivityLayout;
56 import com.android.camera.ui.MarginDrawable;
57 import com.android.camera.ui.ModeListView;
58 import com.android.camera.ui.ModeTransitionView;
59 import com.android.camera.ui.PreviewOverlay;
60 import com.android.camera.ui.PreviewStatusListener;
61 import com.android.camera.ui.StickyBottomCaptureLayout;
62 import com.android.camera.ui.TouchCoordinate;
63 import com.android.camera.ui.focus.FocusRing;
64 import com.android.camera.util.AndroidServices;
65 import com.android.camera.util.ApiHelper;
66 import com.android.camera.util.CameraUtil;
67 import com.android.camera.util.Gusterpolator;
68 import com.android.camera.util.PhotoSphereHelper;
69 import com.android.camera.widget.Cling;
70 import com.android.camera.widget.FilmstripLayout;
71 import com.android.camera.widget.IndicatorIconController;
72 import com.android.camera.widget.ModeOptionsOverlay;
73 import com.android.camera.widget.RoundedThumbnailView;
74 import com.android.camera2.R;
76 import java.util.List;
79 * CameraAppUI centralizes control of views shared across modules. Whereas module
80 * specific views will be handled in each Module UI. For example, we can now
81 * bring the flash animation and capture animation up from each module to app
82 * level, as these animations are largely the same for all modules.
84 * This class also serves to disambiguate touch events. It recognizes all the
85 * swipe gestures that happen on the preview by attaching a touch listener to
86 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
87 * of how swipe from each direction should be handled, it can then redirect these
88 * events to appropriate recipient views.
90 public class CameraAppUI implements ModeListView.ModeSwitchListener,
91 TextureView.SurfaceTextureListener,
92 ModeListView.ModeListOpenListener,
93 SettingsManager.OnSettingChangedListener,
94 ShutterButton.OnShutterButtonListener {
97 * The bottom controls on the filmstrip.
99 public static interface BottomPanel {
100 /** Values for the view state of the button. */
101 public final int VIEWER_NONE = 0;
102 public final int VIEWER_PHOTO_SPHERE = 1;
103 public final int VIEWER_REFOCUS = 2;
104 public final int VIEWER_OTHER = 3;
107 * Sets a new or replaces an existing listener for bottom control events.
109 void setListener(Listener listener);
112 * Sets cling for external viewer button.
114 void setClingForViewer(int viewerType, Cling cling);
117 * Clears cling for external viewer button.
119 void clearClingForViewer(int viewerType);
122 * Returns a cling for the specified viewer type.
124 Cling getClingForViewer(int viewerType);
127 * Set if the bottom controls are visible.
128 * @param visible {@code true} if visible.
130 void setVisible(boolean visible);
133 * @param visible Whether the button is visible.
135 void setEditButtonVisibility(boolean visible);
138 * @param enabled Whether the button is enabled.
140 void setEditEnabled(boolean enabled);
143 * Sets the visibility of the view-photosphere button.
145 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
146 * {@link #VIEWER_REFOCUS}.
148 void setViewerButtonVisibility(int state);
151 * @param enabled Whether the button is enabled.
153 void setViewEnabled(boolean enabled);
156 * @param enabled Whether the button is enabled.
158 void setTinyPlanetEnabled(boolean enabled);
161 * @param visible Whether the button is visible.
163 void setDeleteButtonVisibility(boolean visible);
166 * @param enabled Whether the button is enabled.
168 void setDeleteEnabled(boolean enabled);
171 * @param visible Whether the button is visible.
173 void setShareButtonVisibility(boolean visible);
176 * @param enabled Whether the button is enabled.
178 void setShareEnabled(boolean enabled);
181 * Sets the texts for progress UI.
183 * @param text The text to show.
185 void setProgressText(CharSequence text);
190 * @param progress The progress value. Should be between 0 and 100.
192 void setProgress(int progress);
195 * Replaces the progress UI with an error message.
197 void showProgressError(CharSequence message);
200 * Hide the progress error message.
202 void hideProgressError();
205 * Shows the progress.
210 * Hides the progress.
215 * Shows the controls.
220 * Hides the controls.
225 * Classes implementing this interface can listen for events on the bottom
228 public static interface Listener {
230 * Called when the user pressed the "view" button to e.g. view a photo
231 * sphere or RGBZ image.
233 public void onExternalViewer();
236 * Called when the "edit" button is pressed.
238 public void onEdit();
241 * Called when the "tiny planet" button is pressed.
243 public void onTinyPlanet();
246 * Called when the "delete" button is pressed.
248 public void onDelete();
251 * Called when the "share" button is pressed.
253 public void onShare();
256 * Called when the progress error message is clicked.
258 public void onProgressErrorClicked();
263 * BottomBarUISpec provides a structure for modules
264 * to specify their ideal bottom bar mode options layout.
266 * Once constructed by a module, this class should be
267 * treated as read only.
269 * The application then edits this spec according to
270 * hardware limitations and displays the final bottom
273 public static class BottomBarUISpec {
274 /** Mode options UI */
277 * Set true if the camera option should be enabled.
278 * If not set or false, and multiple cameras are supported,
279 * the camera option will be disabled.
281 * If multiple cameras are not supported, this preference
282 * is ignored and the camera option will not be visible.
284 public boolean enableCamera;
287 * Set true if the camera option should not be visible, regardless
288 * of hardware limitations.
290 public boolean hideCamera;
293 * Set true if the photo flash option should be enabled.
294 * If not set or false, the photo flash option will be
297 * If the hardware does not support multiple flash values,
298 * this preference is ignored and the flash option will
299 * be disabled. It will not be made invisible in order to
300 * preserve a consistent experience across devices and between
301 * front and back cameras.
303 public boolean enableFlash;
306 * Set true if the video flash option should be enabled.
307 * Same disable rules apply as the photo flash option.
309 public boolean enableTorchFlash;
312 * Set true if the HDR+ flash option should be enabled.
313 * Same disable rules apply as the photo flash option.
315 public boolean enableHdrPlusFlash;
318 * Set true if flash should not be visible, regardless of
319 * hardware limitations.
321 public boolean hideFlash;
324 * Set true if the hdr/hdr+ option should be enabled.
325 * If not set or false, the hdr/hdr+ option will be disabled.
327 * Hdr or hdr+ will be chosen based on hardware limitations,
328 * with hdr+ prefered.
330 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
331 * will not be visible.
333 public boolean enableHdr;
336 * Set true if hdr/hdr+ should not be visible, regardless of
337 * hardware limitations.
339 public boolean hideHdr;
342 * Set true if grid lines should be visible. Not setting this
343 * causes grid lines to be disabled. This option is agnostic to
346 public boolean enableGridLines;
349 * Set true if grid lines should not be visible.
351 public boolean hideGridLines;
354 * Set true if the panorama orientation option should be visible.
356 * This option is not constrained by hardware limitations.
358 public boolean enablePanoOrientation;
360 public boolean enableExposureCompensation;
365 * Set true if the intent ui cancel option should be visible.
367 public boolean showCancel;
369 * Set true if the intent ui done option should be visible.
371 public boolean showDone;
373 * Set true if the intent ui retake option should be visible.
375 public boolean showRetake;
377 * Set true if the intent ui review option should be visible.
379 public boolean showReview;
381 /** Mode options callbacks */
384 * A {@link com.android.camera.ButtonManager.ButtonCallback}
385 * that will be executed when the camera option is pressed. This
386 * callback can be null.
388 public ButtonManager.ButtonCallback cameraCallback;
391 * A {@link com.android.camera.ButtonManager.ButtonCallback}
392 * that will be executed when the flash option is pressed. This
393 * callback can be null.
395 public ButtonManager.ButtonCallback flashCallback;
398 * A {@link com.android.camera.ButtonManager.ButtonCallback}
399 * that will be executed when the hdr/hdr+ option is pressed. This
400 * callback can be null.
402 public ButtonManager.ButtonCallback hdrCallback;
405 * A {@link com.android.camera.ButtonManager.ButtonCallback}
406 * that will be executed when the grid lines option is pressed. This
407 * callback can be null.
409 public ButtonManager.ButtonCallback gridLinesCallback;
412 * A {@link com.android.camera.ButtonManager.ButtonCallback}
413 * that will execute when the panorama orientation option is pressed.
414 * This callback can be null.
416 public ButtonManager.ButtonCallback panoOrientationCallback;
418 /** Intent UI callbacks */
421 * A {@link android.view.View.OnClickListener} that will execute
422 * when the cancel option is pressed. This callback can be null.
424 public View.OnClickListener cancelCallback;
427 * A {@link android.view.View.OnClickListener} that will execute
428 * when the done option is pressed. This callback can be null.
430 public View.OnClickListener doneCallback;
433 * A {@link android.view.View.OnClickListener} that will execute
434 * when the retake option is pressed. This callback can be null.
436 public View.OnClickListener retakeCallback;
439 * A {@link android.view.View.OnClickListener} that will execute
440 * when the review option is pressed. This callback can be null.
442 public View.OnClickListener reviewCallback;
445 * A ExposureCompensationSetCallback that will execute
446 * when an expsosure button is pressed. This callback can be null.
448 public interface ExposureCompensationSetCallback {
449 public void setExposure(int value);
451 public ExposureCompensationSetCallback exposureCompensationSetCallback;
454 * Exposure compensation parameters.
456 public int minExposureCompensation;
457 public int maxExposureCompensation;
458 public float exposureCompensationStep;
461 * Whether self-timer is enabled.
463 public boolean enableSelfTimer = false;
466 * Whether the option for self-timer should show. If true and
467 * {@link #enableSelfTimer} is false, then the option should be shown
470 public boolean showSelfTimer = false;
474 private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
476 private final AppController mController;
477 private final boolean mIsCaptureIntent;
478 private final AnimationManager mAnimationManager;
481 private final static int IDLE = 0;
482 private final static int SWIPE_UP = 1;
483 private final static int SWIPE_DOWN = 2;
484 private final static int SWIPE_LEFT = 3;
485 private final static int SWIPE_RIGHT = 4;
486 private boolean mSwipeEnabled = true;
488 // Shared Surface Texture properities.
489 private SurfaceTexture mSurface;
490 private int mSurfaceWidth;
491 private int mSurfaceHeight;
493 // Touch related measures:
494 private final int mSlop;
495 private final static int SWIPE_TIME_OUT_MS = 500;
497 // Mode cover states:
498 private final static int COVER_HIDDEN = 0;
499 private final static int COVER_SHOWN = 1;
500 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
501 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
504 * Preview down-sample rate when taking a screenshot.
506 private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
509 private final FrameLayout mCameraRootView;
510 private final ModeTransitionView mModeTransitionView;
511 private final MainActivityLayout mAppRootView;
512 private final ModeListView mModeListView;
513 private final FilmstripLayout mFilmstripLayout;
514 private TextureView mTextureView;
515 private FrameLayout mModuleUI;
516 private ShutterButton mShutterButton;
517 private ImageButton mCountdownCancelButton;
518 private BottomBar mBottomBar;
519 private ModeOptionsOverlay mModeOptionsOverlay;
520 private IndicatorIconController mIndicatorIconController;
521 private FocusRing mFocusRing;
522 private FrameLayout mTutorialsPlaceHolderWrapper;
523 private StickyBottomCaptureLayout mStickyBottomCaptureLayout;
524 private TextureViewHelper mTextureViewHelper;
525 private final GestureDetector mGestureDetector;
526 private DisplayManager.DisplayListener mDisplayListener;
527 private int mLastRotation;
528 private int mSwipeState = IDLE;
529 private PreviewOverlay mPreviewOverlay;
530 private GridLines mGridLines;
531 private CaptureAnimationOverlay mCaptureOverlay;
532 private PreviewStatusListener mPreviewStatusListener;
533 private int mModeCoverState = COVER_HIDDEN;
534 private final FilmstripBottomPanel mFilmstripBottomControls;
535 private final FilmstripContentPanel mFilmstripPanel;
536 private Runnable mHideCoverRunnable;
537 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
538 = new View.OnLayoutChangeListener() {
540 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
541 int oldTop, int oldRight, int oldBottom) {
542 if (mPreviewStatusListener != null) {
543 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
544 oldTop, oldRight, oldBottom);
548 private View mModeOptionsToggle;
549 private final RoundedThumbnailView mRoundedThumbnailView;
550 private final CaptureLayoutHelper mCaptureLayoutHelper;
551 private boolean mAccessibilityEnabled;
552 private final View mAccessibilityAffordances;
554 private boolean mDisableAllUserInteractions;
555 /** Whether to prevent capture indicator from being triggered. */
556 private boolean mSuppressCaptureIndicator;
559 * Provides current preview frame and the controls/overlay from the module that
560 * are shown on top of the preview.
562 public interface CameraModuleScreenShotProvider {
564 * Returns the current preview frame down-sampled using the given down-sample
567 * @param downSampleFactor the down sample factor for down sampling the
568 * preview frame. (e.g. a down sample factor of
569 * 2 means to scale down the preview frame to 1/2
570 * the width and height.)
571 * @return down-sampled preview frame
573 public Bitmap getPreviewFrame(int downSampleFactor);
576 * @return the controls and overlays that are currently showing on top of
577 * the preview drawn into a bitmap with no scaling applied.
579 public Bitmap getPreviewOverlayAndControls();
582 * Returns a bitmap containing the current screenshot.
584 * @param previewDownSampleFactor the downsample factor applied on the
585 * preview frame when taking the screenshot
587 public Bitmap getScreenShot(int previewDownSampleFactor);
591 * This listener gets called when the size of the window (excluding the system
592 * decor such as status bar and nav bar) has changed.
594 public interface NonDecorWindowSizeChangedListener {
595 public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
598 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
599 new CameraModuleScreenShotProvider() {
601 public Bitmap getPreviewFrame(int downSampleFactor) {
602 if (mCameraRootView == null || mTextureView == null) {
605 // Gets the bitmap from the preview TextureView.
606 Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
611 public Bitmap getPreviewOverlayAndControls() {
612 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
613 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
614 Canvas canvas = new Canvas(overlays);
615 mCameraRootView.draw(canvas);
620 public Bitmap getScreenShot(int previewDownSampleFactor) {
621 Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
622 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
623 Canvas canvas = new Canvas(screenshot);
624 canvas.drawARGB(255, 0, 0, 0);
625 Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
626 if (preview != null) {
627 canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
629 Bitmap overlay = getPreviewOverlayAndControls();
630 if (overlay != null) {
631 canvas.drawBitmap(overlay, 0f, 0f, null);
637 private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
639 public long getCoverHiddenTime() {
640 return mCoverHiddenTime;
644 * This resets the preview to have no applied transform matrix.
646 public void clearPreviewTransform() {
647 mTextureViewHelper.clearTransform();
650 public void updatePreviewAspectRatio(float aspectRatio) {
651 mTextureViewHelper.updateAspectRatio(aspectRatio);
655 * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
656 * its TextureView. This is necessary to get the expected behavior for the TextureView's
657 * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
658 * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
661 * b/17286155 - Tracking a fix for this in HardwareLayer.
663 public void setDefaultBufferSizeToViewDimens() {
664 if (mSurface == null || mTextureView == null) {
665 Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
668 mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
672 * Updates the preview matrix without altering it.
675 * @param aspectRatio the desired aspect ratio for the preview.
677 public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
678 mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
682 * @return the rect that will display the preview.
684 public RectF getFullscreenRect() {
685 return mTextureViewHelper.getFullscreenRect();
689 * This is to support modules that calculate their own transform matrix because
690 * they need to use a transform matrix to rotate the preview.
692 * @param matrix transform matrix to be set on the TextureView
694 public void updatePreviewTransform(Matrix matrix) {
695 mTextureViewHelper.updateTransform(matrix);
698 public interface AnimationFinishedListener {
699 public void onAnimationFinished(boolean success);
702 private class MyTouchListener implements View.OnTouchListener {
703 private boolean mScaleStarted = false;
705 public boolean onTouch(View v, MotionEvent event) {
706 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
707 mScaleStarted = false;
708 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
709 mScaleStarted = true;
711 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
716 * This gesture listener finds out the direction of the scroll gestures and
717 * sends them to CameraAppUI to do further handling.
719 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
720 private MotionEvent mDown;
723 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
724 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
725 || mSwipeState != IDLE
731 int deltaX = (int) (ev.getX() - mDown.getX());
732 int deltaY = (int) (ev.getY() - mDown.getY());
733 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
734 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
735 // Calculate the direction of the swipe.
736 if (deltaX >= Math.abs(deltaY)) {
738 setSwipeState(SWIPE_RIGHT);
739 } else if (deltaX <= -Math.abs(deltaY)) {
741 setSwipeState(SWIPE_LEFT);
748 private void setSwipeState(int swipeState) {
749 mSwipeState = swipeState;
750 // Notify new swipe detected.
751 onSwipeDetected(swipeState);
755 public boolean onDown(MotionEvent ev) {
756 mDown = MotionEvent.obtain(ev);
762 public CameraAppUI(AppController controller, MainActivityLayout appRootView,
763 boolean isCaptureIntent) {
764 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
765 mController = controller;
766 mIsCaptureIntent = isCaptureIntent;
768 mAppRootView = appRootView;
769 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
770 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
771 mModeTransitionView = (ModeTransitionView)
772 mAppRootView.findViewById(R.id.mode_transition_view);
773 mFilmstripBottomControls = new FilmstripBottomPanel(controller,
774 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
775 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
776 mGestureDetector = new GestureDetector(controller.getAndroidContext(),
777 new MyGestureListener());
778 Resources res = controller.getAndroidContext().getResources();
779 mCaptureLayoutHelper = new CaptureLayoutHelper(
780 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
781 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
782 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
783 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
784 if (mModeListView != null) {
785 mModeListView.setModeSwitchListener(this);
786 mModeListView.setModeListOpenListener(this);
787 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
788 mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
789 boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
790 SettingsManager.SCOPE_GLOBAL,
791 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
792 mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
794 Log.e(TAG, "Cannot find mode list in the view hierarchy");
796 mAnimationManager = new AnimationManager();
797 mRoundedThumbnailView = (RoundedThumbnailView) appRootView.findViewById(R.id.rounded_thumbnail_view);
798 mRoundedThumbnailView.setCallback(new RoundedThumbnailView.Callback() {
800 public void onHitStateFinished() {
801 mFilmstripLayout.showFilmstrip();
805 mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
806 initDisplayListener();
807 mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
808 View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
809 modeListToggle.setOnClickListener(new View.OnClickListener() {
811 public void onClick(View view) {
815 View filmstripToggle = mAppRootView.findViewById(
816 R.id.accessibility_filmstrip_toggle_button);
817 filmstripToggle.setOnClickListener(new View.OnClickListener() {
819 public void onClick(View view) {
824 mSuppressCaptureIndicator = false;
829 * Freeze what is currently shown on screen until the next preview frame comes
832 public void freezeScreenUntilPreviewReady() {
833 Log.v(TAG, "freezeScreenUntilPreviewReady");
834 mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
835 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
836 mHideCoverRunnable = new Runnable() {
839 mModeTransitionView.hideImageCover();
842 mModeCoverState = COVER_SHOWN;
846 * Creates a cling for the specific viewer and links the cling to the corresponding
847 * button for layout position.
849 * @param viewerType defines which viewer the cling is for.
851 public void setupClingForViewer(int viewerType) {
852 if (viewerType == BottomPanel.VIEWER_REFOCUS) {
853 FrameLayout filmstripContent = (FrameLayout) mAppRootView
854 .findViewById(R.id.camera_filmstrip_content_layout);
855 if (filmstripContent != null) {
856 // Creates refocus cling.
857 LayoutInflater inflater = AndroidServices.instance().provideLayoutInflater();
858 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
859 // Sets instruction text in the cling.
860 refocusCling.setText(mController.getAndroidContext().getResources()
861 .getString(R.string.cling_text_for_refocus_editor_button));
863 // Adds cling into view hierarchy.
864 int clingWidth = mController.getAndroidContext()
865 .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
866 filmstripContent.addView(refocusCling, clingWidth,
867 ViewGroup.LayoutParams.WRAP_CONTENT);
868 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
874 * Clears the listeners for the cling and remove it from the view hierarchy.
876 * @param viewerType defines which viewer the cling is for.
878 public void clearClingForViewer(int viewerType) {
879 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
880 if (clingToBeRemoved == null) {
881 // No cling is created for the specific viewer type.
884 mFilmstripBottomControls.clearClingForViewer(viewerType);
885 clingToBeRemoved.setVisibility(View.GONE);
886 mAppRootView.removeView(clingToBeRemoved);
890 * Enable or disable swipe gestures. We want to disable them e.g. while we
893 public void setSwipeEnabled(boolean enabled) {
894 mSwipeEnabled = enabled;
895 // TODO: This can be removed once we come up with a new design for handling swipe
896 // on shutter button and mode options. (More details: b/13751653)
897 mAppRootView.setSwipeEnabled(enabled);
900 public void onDestroy() {
901 AndroidServices.instance().provideDisplayManager()
902 .unregisterDisplayListener(mDisplayListener);
906 * Initializes the display listener to listen to display changes such as
907 * 180-degree rotation change, which will not have an onConfigurationChanged
910 private void initDisplayListener() {
911 if (ApiHelper.HAS_DISPLAY_LISTENER) {
912 mLastRotation = CameraUtil.getDisplayRotation();
914 mDisplayListener = new DisplayManager.DisplayListener() {
916 public void onDisplayAdded(int arg0) {
921 public void onDisplayChanged(int displayId) {
922 int rotation = CameraUtil.getDisplayRotation(
924 if ((rotation - mLastRotation + 360) % 360 == 180
925 && mPreviewStatusListener != null) {
926 mPreviewStatusListener.onPreviewFlipped();
927 mStickyBottomCaptureLayout.requestLayout();
928 mModeListView.requestLayout();
929 mTextureView.requestLayout();
931 mLastRotation = rotation;
935 public void onDisplayRemoved(int arg0) {
940 AndroidServices.instance().provideDisplayManager()
941 .registerDisplayListener(mDisplayListener, null);
946 * Redirects touch events to appropriate recipient views based on swipe direction.
947 * More specifically, swipe up and swipe down will be handled by the view that handles
948 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
949 * to mode list in order to bring up mode list.
951 private void onSwipeDetected(int swipeState) {
952 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
953 // TODO: Polish quick switch after this release.
954 // Quick switch between modes.
955 int currentModuleIndex = mController.getCurrentModuleIndex();
956 final int moduleToTransitionTo =
957 mController.getQuickSwitchToModuleId(currentModuleIndex);
958 if (currentModuleIndex != moduleToTransitionTo) {
959 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
960 int shadeColorId = R.color.camera_gray_background;
961 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
962 mController.getAndroidContext());
964 AnimationFinishedListener listener = new AnimationFinishedListener() {
966 public void onAnimationFinished(boolean success) {
968 mHideCoverRunnable = new Runnable() {
971 mModeTransitionView.startPeepHoleAnimation();
974 mModeCoverState = COVER_SHOWN;
975 // Go to new module when the previous operation is successful.
976 mController.onModeSelected(moduleToTransitionTo);
981 } else if (swipeState == SWIPE_LEFT) {
982 // Pass the touch sequence to filmstrip layout.
983 mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
984 } else if (swipeState == SWIPE_RIGHT) {
985 // Pass the touch to mode switcher
986 mAppRootView.redirectTouchEventsTo(mModeListView);
991 * Gets called when activity resumes in preview.
993 public void resume() {
994 // Show mode theme cover until preview is ready
995 showModeCoverUntilPreviewReady();
997 // Hide action bar first since we are in full screen mode first, and
998 // switch the system UI to lights-out mode.
999 mFilmstripPanel.hide();
1001 // Show UI that is meant to only be used when spoken feedback is
1003 mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
1004 mAccessibilityAffordances.setVisibility(
1005 (!mIsCaptureIntent && mAccessibilityEnabled) ? View.VISIBLE : View.GONE);
1009 * @return Whether any spoken feedback accessibility feature is currently
1012 private boolean isSpokenFeedbackAccessibilityEnabled() {
1013 AccessibilityManager accessibilityManager = AndroidServices.instance()
1014 .provideAccessibilityManager();
1015 List<AccessibilityServiceInfo> infos = accessibilityManager
1016 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1017 return infos != null && !infos.isEmpty();
1021 * Opens the mode list (e.g. because of the menu button being pressed) and
1022 * adapts the rest of the UI.
1024 public void openModeList() {
1025 mModeOptionsOverlay.closeModeOptions();
1026 mModeListView.onMenuPressed();
1030 * A cover view showing the mode theme color and mode icon will be visible on
1031 * top of preview until preview is ready (i.e. camera preview is started and
1032 * the first frame has been received).
1034 private void showModeCoverUntilPreviewReady() {
1035 int modeId = mController.getCurrentModuleIndex();
1036 int colorId = R.color.camera_gray_background;;
1037 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1038 mModeTransitionView.setupModeCover(colorId, iconId);
1039 mHideCoverRunnable = new Runnable() {
1042 mModeTransitionView.hideModeCover(null);
1043 if (!mDisableAllUserInteractions) {
1044 showShimmyDelayed();
1048 mModeCoverState = COVER_SHOWN;
1051 private void showShimmyDelayed() {
1052 if (!mIsCaptureIntent) {
1053 // Show shimmy in SHIMMY_DELAY_MS
1054 mModeListView.showModeSwitcherHint();
1058 private void hideModeCover() {
1059 if (mHideCoverRunnable != null) {
1060 mAppRootView.post(mHideCoverRunnable);
1061 mHideCoverRunnable = null;
1063 mModeCoverState = COVER_HIDDEN;
1064 if (mCoverHiddenTime < 0) {
1065 mCoverHiddenTime = System.currentTimeMillis();
1070 public void onPreviewVisiblityChanged(int visibility) {
1071 if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1072 setIndicatorBottomBarWrapperVisible(false);
1073 mAccessibilityAffordances.setVisibility(View.GONE);
1075 setIndicatorBottomBarWrapperVisible(true);
1076 if (!mIsCaptureIntent && mAccessibilityEnabled) {
1077 mAccessibilityAffordances.setVisibility(View.VISIBLE);
1079 mAccessibilityAffordances.setVisibility(View.GONE);
1085 * Call to stop the preview from being rendered. Sets the entire capture
1086 * root view to invisible which includes the preview plus focus indicator
1087 * and any other auxiliary views for capture modes.
1089 public void pausePreviewRendering() {
1090 mCameraRootView.setVisibility(View.INVISIBLE);
1094 * Call to begin rendering the preview and auxiliary views again.
1096 public void resumePreviewRendering() {
1097 mCameraRootView.setVisibility(View.VISIBLE);
1101 * Returns the transform associated with the preview view.
1103 * @param m the Matrix in which to copy the current transform.
1104 * @return The specified matrix if not null or a new Matrix instance
1107 public Matrix getPreviewTransform(Matrix m) {
1108 return mTextureView.getTransform(m);
1112 public void onOpenFullScreen() {
1117 public void onModeListOpenProgress(float progress) {
1118 progress = 1 - progress;
1119 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1120 mModeOptionsToggle.setAlpha(interpolatedProgress);
1121 // Change shutter button alpha linearly based on the mode list open progress:
1122 // set the alpha to disabled alpha when list is fully open, to enabled alpha
1123 // when the list is fully closed.
1124 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1125 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1129 public void onModeListClosed() {
1130 // Make sure the alpha on mode options ellipse is reset when mode drawer
1132 mModeOptionsToggle.setAlpha(1f);
1133 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1137 * Called when the back key is pressed.
1139 * @return Whether the UI responded to the key event.
1141 public boolean onBackPressed() {
1142 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1143 return mFilmstripLayout.onBackPressed();
1145 return mModeListView.onBackPressed();
1150 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1151 * listens to SurfaceTexture changes. In addition, listeners are set on
1152 * dependent app ui elements.
1154 * @param previewStatusListener the listener that gets notified when SurfaceTexture
1157 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1158 mPreviewStatusListener = previewStatusListener;
1159 if (mPreviewStatusListener != null) {
1160 onPreviewListenerChanged();
1165 * When the PreviewStatusListener changes, listeners need to be
1166 * set on the following app ui elements:
1167 * {@link com.android.camera.ui.PreviewOverlay},
1168 * {@link com.android.camera.ui.BottomBar},
1169 * {@link com.android.camera.ui.IndicatorIconController}.
1171 private void onPreviewListenerChanged() {
1172 // Set a listener for recognizing preview gestures.
1173 GestureDetector.OnGestureListener gestureListener
1174 = mPreviewStatusListener.getGestureListener();
1175 if (gestureListener != null) {
1176 mPreviewOverlay.setGestureListener(gestureListener);
1178 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1179 if (touchListener != null) {
1180 mPreviewOverlay.setTouchListener(touchListener);
1183 mTextureViewHelper.setAutoAdjustTransform(
1184 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1188 * This method should be called in onCameraOpened. It defines CameraAppUI
1189 * specific changes that depend on the camera or camera settings.
1191 public void onChangeCamera() {
1192 ModuleController moduleController = mController.getCurrentModuleController();
1193 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1195 if (mIndicatorIconController != null) {
1196 // Sync the settings state with the indicator state.
1197 mIndicatorIconController.syncIndicators();
1202 * Adds a listener to receive callbacks when preview area changes.
1204 public void addPreviewAreaChangedListener(
1205 PreviewStatusListener.PreviewAreaChangedListener listener) {
1206 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1210 * Removes a listener that receives callbacks when preview area changes.
1212 public void removePreviewAreaChangedListener(
1213 PreviewStatusListener.PreviewAreaChangedListener listener) {
1214 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1218 * This inflates generic_module layout, which contains all the shared views across
1219 * modules. Then each module inflates their own views in the given view group. For
1220 * now, this is called every time switching from a not-yet-refactored module to a
1221 * refactored module. In the future, this should only need to be done once per app
1224 public void prepareModuleUI() {
1225 mController.getSettingsManager().addListener(this);
1226 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1227 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1228 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1229 mController.getCameraProvider());
1230 mTextureViewHelper.setSurfaceTextureListener(this);
1231 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1233 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1234 int unpressedColor = mController.getAndroidContext().getResources()
1235 .getColor(R.color.camera_gray_background);
1236 setBottomBarColor(unpressedColor);
1237 updateModeSpecificUIColors();
1239 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1242 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1244 // Sets the visibility of the bottom bar and the mode options.
1245 resetBottomControls(mController.getCurrentModuleController(),
1246 mController.getCurrentModuleIndex());
1247 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1249 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1250 addShutterListener(mController.getCurrentModuleController());
1251 addShutterListener(mModeOptionsOverlay);
1252 addShutterListener(this);
1254 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1255 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1257 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1258 mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1259 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1261 mCaptureOverlay = (CaptureAnimationOverlay)
1262 mCameraRootView.findViewById(R.id.capture_overlay);
1263 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1264 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1266 if (mIndicatorIconController == null) {
1267 mIndicatorIconController =
1268 new IndicatorIconController(mController, mAppRootView);
1271 mController.getButtonManager().load(mCameraRootView);
1272 mController.getButtonManager().setListener(mIndicatorIconController);
1273 mController.getSettingsManager().addListener(mIndicatorIconController);
1275 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1276 mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1277 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1278 .findViewById(R.id.tutorials_placeholder_wrapper);
1279 mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1280 .findViewById(R.id.sticky_bottom_capture_layout);
1281 mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1282 mCountdownCancelButton = (ImageButton) mStickyBottomCaptureLayout
1283 .findViewById(R.id.shutter_cancel_button);
1285 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1286 mTextureViewHelper.addAspectRatioChangedListener(
1287 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1289 public void onPreviewAspectRatioChanged(float aspectRatio) {
1290 mModeOptionsOverlay.requestLayout();
1291 mBottomBar.requestLayout();
1298 * Called indirectly from each module in their initialization to get a view group
1299 * to inflate the module specific views in.
1301 * @return a view group for modules to attach views to
1303 public FrameLayout getModuleRootView() {
1304 // TODO: Change it to mModuleUI when refactor is done
1305 return mCameraRootView;
1309 * Remove all the module specific views.
1311 public void clearModuleUI() {
1312 if (mModuleUI != null) {
1313 mModuleUI.removeAllViews();
1315 removeShutterListener(mController.getCurrentModuleController());
1316 mTutorialsPlaceHolderWrapper.removeAllViews();
1317 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1319 setShutterButtonEnabled(true);
1320 mPreviewStatusListener = null;
1321 mPreviewOverlay.reset();
1323 Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1324 mFocusRing.stopFocusAnimations();
1328 * Gets called when preview is ready to start. It sets up one shot preview callback
1329 * in order to receive a callback when the preview frame is available, so that
1330 * the preview cover can be hidden to reveal preview.
1332 * An alternative for getting the timing to hide preview cover is through
1333 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1334 * which is less accurate but therefore is the fallback for modules that manage
1335 * their own preview callbacks (as setting one preview callback will override
1336 * any other installed preview callbacks), or use camera2 API.
1338 public void onPreviewReadyToStart() {
1339 if (mModeCoverState == COVER_SHOWN) {
1340 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1341 mController.setupOneShotPreviewListener();
1346 * Gets called when preview is started.
1348 public void onPreviewStarted() {
1349 Log.v(TAG, "onPreviewStarted");
1350 if (mModeCoverState == COVER_SHOWN) {
1351 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1353 enableModeOptions();
1357 * Gets notified when next preview frame comes in.
1359 public void onNewPreviewFrame() {
1360 Log.v(TAG, "onNewPreviewFrame");
1361 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1366 public void onShutterButtonClick() {
1368 * Set the mode options toggle unclickable, generally
1369 * throughout the app, whenever the shutter button is clicked.
1371 * This could be done in the OnShutterButtonListener of the
1372 * ModeOptionsOverlay, but since it is very important that we
1373 * can clearly see when the toggle becomes clickable again,
1374 * keep all of that logic at this level.
1376 // disableModeOptions();
1380 public void onShutterCoordinate(TouchCoordinate coord) {
1385 public void onShutterButtonFocus(boolean pressed) {
1390 public void onShutterButtonLongPressed() {
1395 * Set the mode options toggle clickable.
1397 public void enableModeOptions() {
1399 * For modules using camera 1 api, this gets called in
1400 * onSurfaceTextureUpdated whenever the preview gets stopped and
1401 * started after each capture. This also takes care of the
1402 * case where the mode options might be unclickable when we
1405 * For modules using camera 2 api, they're required to call this
1406 * method when a capture is "completed". Unfortunately this differs
1407 * per module implementation.
1409 if (!mDisableAllUserInteractions) {
1410 mModeOptionsOverlay.setToggleClickable(true);
1415 * Set the mode options toggle not clickable.
1417 public void disableModeOptions() {
1418 mModeOptionsOverlay.setToggleClickable(false);
1421 public void setDisableAllUserInteractions(boolean disable) {
1423 disableModeOptions();
1424 setShutterButtonEnabled(false);
1425 setSwipeEnabled(false);
1426 mModeListView.hideAnimated();
1428 enableModeOptions();
1429 setShutterButtonEnabled(true);
1430 setSwipeEnabled(true);
1432 mDisableAllUserInteractions = disable;
1436 public void onModeButtonPressed(int modeIndex) {
1437 // TODO: Make CameraActivity listen to ModeListView's events.
1438 int pressedModuleId = mController.getModuleId(modeIndex);
1439 int currentModuleId = mController.getCurrentModuleIndex();
1440 if (pressedModuleId != currentModuleId) {
1441 hideCaptureIndicator();
1446 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1448 * @param modeIndex mode index of the selected mode
1451 public void onModeSelected(int modeIndex) {
1452 mHideCoverRunnable = new Runnable() {
1455 mModeListView.startModeSelectionAnimation();
1458 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1459 mModeCoverState = COVER_SHOWN;
1461 int lastIndex = mController.getCurrentModuleIndex();
1462 // Actual mode teardown / new mode initialization happens here
1463 mController.onModeSelected(modeIndex);
1464 int currentIndex = mController.getCurrentModuleIndex();
1466 if (lastIndex == currentIndex) {
1470 updateModeSpecificUIColors();
1473 private void updateModeSpecificUIColors() {
1474 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1478 public void onSettingsSelected() {
1479 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1480 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1481 mModeListView.setShouldShowSettingsCling(false);
1482 mController.onSettingsSelected();
1486 public int getCurrentModeIndex() {
1487 return mController.getCurrentModuleIndex();
1490 /********************** Capture animation **********************/
1491 /* TODO: This session is subject to UX changes. In addition to the generic
1492 flash animation and post capture animation, consider designating a parameter
1493 for specifying the type of animation, as well as an animation finished listener
1494 so that modules can have more knowledge of the status of the animation. */
1497 * Turns on or off the capture indicator suppression.
1499 public void setShouldSuppressCaptureIndicator(boolean suppress) {
1500 mSuppressCaptureIndicator = suppress;
1504 * Starts the capture indicator pop-out animation.
1506 * @param accessibilityString An accessibility String to be announced during the peek animation.
1508 public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1509 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
1512 mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1516 * Updates the thumbnail image in the capture indicator.
1518 * @param thumbnailBitmap The thumbnail image to be shown.
1520 public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
1521 if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
1524 mRoundedThumbnailView.setThumbnail(thumbnailBitmap);
1525 mRoundedThumbnailView.setRotation(rotation);
1529 * Hides the capture indicator.
1531 public void hideCaptureIndicator() {
1532 mRoundedThumbnailView.hideThumbnail();
1536 * Starts the flash animation.
1538 public void startFlashAnimation(boolean shortFlash) {
1539 mCaptureOverlay.startFlashAnimation(shortFlash);
1543 * Cancels the pre-capture animation.
1545 public void cancelPreCaptureAnimation() {
1546 mAnimationManager.cancelAnimations();
1550 * Cancels the post-capture animation.
1552 public void cancelPostCaptureAnimation() {
1553 mAnimationManager.cancelAnimations();
1556 public FilmstripContentPanel getFilmstripContentPanel() {
1557 return mFilmstripPanel;
1561 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1562 * bottom of the filmstrip.
1564 public BottomPanel getFilmstripBottomControls() {
1565 return mFilmstripBottomControls;
1568 public void showBottomControls() {
1569 mFilmstripBottomControls.show();
1572 public void hideBottomControls() {
1573 mFilmstripBottomControls.hide();
1577 * @param listener The listener for bottom controls.
1579 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1580 mFilmstripBottomControls.setListener(listener);
1583 /***************************SurfaceTexture Api and Listener*********************************/
1586 * Return the shared surface texture.
1588 public SurfaceTexture getSurfaceTexture() {
1593 * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1595 public int getSurfaceWidth() {
1596 return mSurfaceWidth;
1600 * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1602 public int getSurfaceHeight() {
1603 return mSurfaceHeight;
1607 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1609 mSurfaceWidth = width;
1610 mSurfaceHeight = height;
1611 Log.v(TAG, "SurfaceTexture is available");
1612 if (mPreviewStatusListener != null) {
1613 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1615 enableModeOptions();
1619 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1621 mSurfaceWidth = width;
1622 mSurfaceHeight = height;
1623 if (mPreviewStatusListener != null) {
1624 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1629 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1631 Log.v(TAG, "SurfaceTexture is destroyed");
1632 if (mPreviewStatusListener != null) {
1633 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1639 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1641 if (mPreviewStatusListener != null) {
1642 mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1644 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1645 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1646 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1651 /****************************Grid lines api ******************************/
1654 * Show a set of evenly spaced lines over the preview. The number
1655 * of lines horizontally and vertically is determined by
1656 * {@link com.android.camera.ui.GridLines}.
1658 public void showGridLines() {
1659 if (mGridLines != null) {
1660 mGridLines.setVisibility(View.VISIBLE);
1665 * Hide the set of evenly spaced grid lines overlaying the preview.
1667 public void hideGridLines() {
1668 if (mGridLines != null) {
1669 mGridLines.setVisibility(View.INVISIBLE);
1674 * Return a callback which shows or hide the preview grid lines
1675 * depending on whether the grid lines setting is set on.
1677 public ButtonManager.ButtonCallback getGridLinesCallback() {
1678 return new ButtonManager.ButtonCallback() {
1680 public void onStateChanged(int state) {
1681 if (!mController.isPaused()) {
1682 if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1692 /***************************Mode options api *****************************/
1695 * Set the mode options visible.
1697 public void showModeOptions() {
1698 /* Make mode options clickable. */
1699 enableModeOptions();
1700 mModeOptionsOverlay.setVisibility(View.VISIBLE);
1704 * Set the mode options invisible. This is necessary for modes
1705 * that don't show a bottom bar for the capture UI.
1707 public void hideModeOptions() {
1708 mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1711 /****************************Bottom bar api ******************************/
1714 * Sets up the bottom bar and mode options with the correct
1715 * shutter button and visibility based on the current module.
1717 public void resetBottomControls(ModuleController module, int moduleIndex) {
1718 if (areBottomControlsUsed(module)) {
1719 setBottomBarShutterIcon(moduleIndex);
1720 mCaptureLayoutHelper.setShowBottomBar(true);
1722 mCaptureLayoutHelper.setShowBottomBar(false);
1727 * Show or hide the mode options and bottom bar, based on
1728 * whether the current module is using the bottom bar. Returns
1729 * whether the mode options and bottom bar are used.
1731 private boolean areBottomControlsUsed(ModuleController module) {
1732 if (module.isUsingBottomBar()) {
1744 * Set the bottom bar visible.
1746 public void showBottomBar() {
1747 mBottomBar.setVisibility(View.VISIBLE);
1751 * Set the bottom bar invisible.
1753 public void hideBottomBar() {
1754 mBottomBar.setVisibility(View.INVISIBLE);
1758 * Sets the color of the bottom bar.
1760 public void setBottomBarColor(int colorId) {
1761 mBottomBar.setBackgroundColor(colorId);
1765 * Sets the pressed color of the bottom bar for a camera mode index.
1767 public void setBottomBarColorsForModeIndex(int index) {
1768 mBottomBar.setColorsForModeIndex(index);
1772 * Sets the shutter button icon on the bottom bar, based on
1775 public void setBottomBarShutterIcon(int modeIndex) {
1776 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1777 mController.getAndroidContext());
1778 mBottomBar.setShutterButtonIcon(shutterIconId);
1781 public void animateBottomBarToVideoStop(int shutterIconId) {
1782 mBottomBar.animateToVideoStop(shutterIconId);
1785 public void animateBottomBarToFullSize(int shutterIconId) {
1786 mBottomBar.animateToFullSize(shutterIconId);
1789 public void setShutterButtonEnabled(final boolean enabled) {
1790 if (!mDisableAllUserInteractions) {
1791 mBottomBar.post(new Runnable() {
1794 mBottomBar.setShutterButtonEnabled(enabled);
1800 public void setShutterButtonImportantToA11y(boolean important) {
1801 mBottomBar.setShutterButtonImportantToA11y(important);
1804 public boolean isShutterButtonEnabled() {
1805 return mBottomBar.isShutterButtonEnabled();
1808 public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1809 mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1813 * Set the visibility of the bottom bar.
1815 // TODO: needed for when panorama is managed by the generic module ui.
1816 public void setBottomBarVisible(boolean visible) {
1817 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1821 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1823 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1824 mShutterButton.addOnShutterButtonListener(listener);
1828 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1830 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1831 mShutterButton.removeOnShutterButtonListener(listener);
1835 * Sets or replaces the "cancel shutter" button listener.
1837 * TODO: Make this part of the interface the same way shutter button
1840 public void setCancelShutterButtonListener(View.OnClickListener listener) {
1841 mCountdownCancelButton.setOnClickListener(listener);
1845 * Performs a transition to the capture layout of the bottom bar.
1847 public void transitionToCapture() {
1848 ModuleController moduleController = mController.getCurrentModuleController();
1849 applyModuleSpecs(moduleController.getHardwareSpec(),
1850 moduleController.getBottomBarSpec());
1851 mBottomBar.transitionToCapture();
1856 * Displays the Cancel button instead of the capture button.
1858 public void transitionToCancel() {
1859 ModuleController moduleController = mController.getCurrentModuleController();
1860 applyModuleSpecs(moduleController.getHardwareSpec(),
1861 moduleController.getBottomBarSpec());
1862 mBottomBar.transitionToCancel();
1867 * Performs a transition to the global intent layout.
1869 public void transitionToIntentCaptureLayout() {
1870 ModuleController moduleController = mController.getCurrentModuleController();
1871 applyModuleSpecs(moduleController.getHardwareSpec(),
1872 moduleController.getBottomBarSpec());
1873 mBottomBar.transitionToIntentCaptureLayout();
1878 * Performs a transition to the global intent review layout.
1880 public void transitionToIntentReviewLayout() {
1881 ModuleController moduleController = mController.getCurrentModuleController();
1882 applyModuleSpecs(moduleController.getHardwareSpec(),
1883 moduleController.getBottomBarSpec());
1884 mBottomBar.transitionToIntentReviewLayout();
1889 * @return whether UI is in intent review mode
1891 public boolean isInIntentReview() {
1892 return mBottomBar.isInIntentReview();
1896 public void onSettingChanged(SettingsManager settingsManager, String key) {
1897 // Update the mode options based on the hardware spec,
1898 // when hdr changes to prevent flash from getting out of sync.
1899 if (key.equals(Keys.KEY_CAMERA_HDR)) {
1900 ModuleController moduleController = mController.getCurrentModuleController();
1901 applyModuleSpecs(moduleController.getHardwareSpec(),
1902 moduleController.getBottomBarSpec());
1907 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1908 * to the bottom bar mode options based on limitations from a
1909 * {@link com.android.camera.hardware.HardwareSpec}.
1911 * Options not supported by the hardware are either hidden
1912 * or disabled, depending on the option.
1914 * Otherwise, the option is fully enabled and clickable.
1916 public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1917 final BottomBarUISpec bottomBarSpec) {
1918 if (hardwareSpec == null || bottomBarSpec == null) {
1922 ButtonManager buttonManager = mController.getButtonManager();
1923 SettingsManager settingsManager = mController.getSettingsManager();
1925 buttonManager.setToInitialState();
1927 /** Standard mode options */
1928 if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1929 hardwareSpec.isFrontCameraSupported()) {
1930 if (bottomBarSpec.enableCamera) {
1931 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1932 bottomBarSpec.cameraCallback);
1934 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1937 // Hide camera icon if front camera not available.
1938 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1941 if (bottomBarSpec.hideFlash) {
1942 // Hide both flash and torch button in flash disable logic
1943 buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1944 buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1946 if (hardwareSpec.isFlashSupported()) {
1947 if (bottomBarSpec.enableFlash) {
1948 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1949 bottomBarSpec.flashCallback);
1950 } else if (bottomBarSpec.enableTorchFlash) {
1951 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1952 bottomBarSpec.flashCallback);
1953 } else if (bottomBarSpec.enableHdrPlusFlash) {
1954 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1955 bottomBarSpec.flashCallback);
1957 // Hide both flash and torch button in flash disable logic
1958 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1959 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1962 // Disable both flash and torch icon if not supported
1963 // by the chosen camera hardware.
1964 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1965 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1969 if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1970 // Force hide hdr or hdr plus icon.
1971 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1973 if (hardwareSpec.isHdrPlusSupported()) {
1974 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1975 mController.getModuleScope())) {
1976 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1977 bottomBarSpec.hdrCallback);
1979 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1981 } else if (hardwareSpec.isHdrSupported()) {
1982 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1983 mController.getModuleScope())) {
1984 buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1985 bottomBarSpec.hdrCallback);
1987 buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1990 // Hide hdr plus or hdr icon if neither are supported.
1991 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1995 if (bottomBarSpec.hideGridLines) {
1996 // Force hide grid lines icon.
1997 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
2000 if (bottomBarSpec.enableGridLines) {
2001 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
2002 bottomBarSpec.gridLinesCallback != null ?
2003 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
2006 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
2011 if (bottomBarSpec.enableSelfTimer) {
2012 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
2014 if (bottomBarSpec.showSelfTimer) {
2015 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
2017 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
2021 if (bottomBarSpec.enablePanoOrientation
2022 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
2023 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
2026 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
2027 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
2028 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
2029 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
2030 if (enableExposureCompensation) {
2031 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION,
2032 new View.OnClickListener() {
2034 public void onClick(View v) {
2035 mModeOptionsOverlay.showExposureOptions();
2038 buttonManager.setExposureCompensationParameters(
2039 bottomBarSpec.minExposureCompensation,
2040 bottomBarSpec.maxExposureCompensation,
2041 bottomBarSpec.exposureCompensationStep);
2043 buttonManager.setExposureCompensationCallback(
2044 bottomBarSpec.exposureCompensationSetCallback);
2045 buttonManager.updateExposureButtons();
2047 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2048 buttonManager.setExposureCompensationCallback(null);
2052 if (bottomBarSpec.showCancel) {
2053 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2054 bottomBarSpec.cancelCallback);
2056 if (bottomBarSpec.showDone) {
2057 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2058 bottomBarSpec.doneCallback);
2060 if (bottomBarSpec.showRetake) {
2061 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2062 bottomBarSpec.retakeCallback,
2064 R.string.retake_button_description);
2066 if (bottomBarSpec.showReview) {
2067 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2068 bottomBarSpec.reviewCallback,
2070 R.string.review_button_description);
2075 * Shows the given tutorial on the screen.
2077 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2078 tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2081 /***************************Filmstrip api *****************************/
2083 public void showFilmstrip() {
2084 mModeListView.onBackPressed();
2085 mFilmstripLayout.showFilmstrip();
2088 public void hideFilmstrip() {
2089 mFilmstripLayout.hideFilmstrip();