OSDN Git Service

Merge "Ensure button callbacks check lifecycle" into ub-camera-haleakala
[android-x86/packages-apps-Camera2.git] / src / com / android / camera / app / CameraAppUI.java
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.camera.app;
18
19 import android.accessibilityservice.AccessibilityServiceInfo;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.graphics.Bitmap;
23 import android.graphics.Canvas;
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
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.ModeListView;
56 import com.android.camera.ui.ModeTransitionView;
57 import com.android.camera.ui.PreviewOverlay;
58 import com.android.camera.ui.PreviewStatusListener;
59 import com.android.camera.ui.StickyBottomCaptureLayout;
60 import com.android.camera.ui.TouchCoordinate;
61 import com.android.camera.ui.focus.FocusRing;
62 import com.android.camera.util.ApiHelper;
63 import com.android.camera.util.CameraUtil;
64 import com.android.camera.util.Gusterpolator;
65 import com.android.camera.util.PhotoSphereHelper;
66 import com.android.camera.widget.Cling;
67 import com.android.camera.widget.FilmstripLayout;
68 import com.android.camera.widget.IndicatorIconController;
69 import com.android.camera.widget.ModeOptionsOverlay;
70 import com.android.camera.widget.RoundedThumbnailView;
71 import com.android.camera2.R;
72
73 import java.util.List;
74
75 /**
76  * CameraAppUI centralizes control of views shared across modules. Whereas module
77  * specific views will be handled in each Module UI. For example, we can now
78  * bring the flash animation and capture animation up from each module to app
79  * level, as these animations are largely the same for all modules.
80  *
81  * This class also serves to disambiguate touch events. It recognizes all the
82  * swipe gestures that happen on the preview by attaching a touch listener to
83  * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
84  * of how swipe from each direction should be handled, it can then redirect these
85  * events to appropriate recipient views.
86  */
87 public class CameraAppUI implements ModeListView.ModeSwitchListener,
88                                     TextureView.SurfaceTextureListener,
89                                     ModeListView.ModeListOpenListener,
90                                     SettingsManager.OnSettingChangedListener,
91                                     ShutterButton.OnShutterButtonListener {
92
93     /**
94      * The bottom controls on the filmstrip.
95      */
96     public static interface BottomPanel {
97         /** Values for the view state of the button. */
98         public final int VIEWER_NONE = 0;
99         public final int VIEWER_PHOTO_SPHERE = 1;
100         public final int VIEWER_REFOCUS = 2;
101         public final int VIEWER_OTHER = 3;
102
103         /**
104          * Sets a new or replaces an existing listener for bottom control events.
105          */
106         void setListener(Listener listener);
107
108         /**
109          * Sets cling for external viewer button.
110          */
111         void setClingForViewer(int viewerType, Cling cling);
112
113         /**
114          * Clears cling for external viewer button.
115          */
116         void clearClingForViewer(int viewerType);
117
118         /**
119          * Returns a cling for the specified viewer type.
120          */
121         Cling getClingForViewer(int viewerType);
122
123         /**
124          * Set if the bottom controls are visible.
125          * @param visible {@code true} if visible.
126          */
127         void setVisible(boolean visible);
128
129         /**
130          * @param visible Whether the button is visible.
131          */
132         void setEditButtonVisibility(boolean visible);
133
134         /**
135          * @param enabled Whether the button is enabled.
136          */
137         void setEditEnabled(boolean enabled);
138
139         /**
140          * Sets the visibility of the view-photosphere button.
141          *
142          * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
143          *            {@link #VIEWER_REFOCUS}.
144          */
145         void setViewerButtonVisibility(int state);
146
147         /**
148          * @param enabled Whether the button is enabled.
149          */
150         void setViewEnabled(boolean enabled);
151
152         /**
153          * @param enabled Whether the button is enabled.
154          */
155         void setTinyPlanetEnabled(boolean enabled);
156
157         /**
158          * @param visible Whether the button is visible.
159          */
160         void setDeleteButtonVisibility(boolean visible);
161
162         /**
163          * @param enabled Whether the button is enabled.
164          */
165         void setDeleteEnabled(boolean enabled);
166
167         /**
168          * @param visible Whether the button is visible.
169          */
170         void setShareButtonVisibility(boolean visible);
171
172         /**
173          * @param enabled Whether the button is enabled.
174          */
175         void setShareEnabled(boolean enabled);
176
177         /**
178          * Sets the texts for progress UI.
179          *
180          * @param text The text to show.
181          */
182         void setProgressText(CharSequence text);
183
184         /**
185          * Sets the progress.
186          *
187          * @param progress The progress value. Should be between 0 and 100.
188          */
189         void setProgress(int progress);
190
191         /**
192          * Replaces the progress UI with an error message.
193          */
194         void showProgressError(CharSequence message);
195
196         /**
197          * Hide the progress error message.
198          */
199         void hideProgressError();
200
201         /**
202          * Shows the progress.
203          */
204         void showProgress();
205
206         /**
207          * Hides the progress.
208          */
209         void hideProgress();
210
211         /**
212          * Shows the controls.
213          */
214         void showControls();
215
216         /**
217          * Hides the controls.
218          */
219         void hideControls();
220
221         /**
222          * Classes implementing this interface can listen for events on the bottom
223          * controls.
224          */
225         public static interface Listener {
226             /**
227              * Called when the user pressed the "view" button to e.g. view a photo
228              * sphere or RGBZ image.
229              */
230             public void onExternalViewer();
231
232             /**
233              * Called when the "edit" button is pressed.
234              */
235             public void onEdit();
236
237             /**
238              * Called when the "tiny planet" button is pressed.
239              */
240             public void onTinyPlanet();
241
242             /**
243              * Called when the "delete" button is pressed.
244              */
245             public void onDelete();
246
247             /**
248              * Called when the "share" button is pressed.
249              */
250             public void onShare();
251
252             /**
253              * Called when the progress error message is clicked.
254              */
255             public void onProgressErrorClicked();
256         }
257     }
258
259     /**
260      * BottomBarUISpec provides a structure for modules
261      * to specify their ideal bottom bar mode options layout.
262      *
263      * Once constructed by a module, this class should be
264      * treated as read only.
265      *
266      * The application then edits this spec according to
267      * hardware limitations and displays the final bottom
268      * bar ui.
269      */
270     public static class BottomBarUISpec {
271         /** Mode options UI */
272
273         /**
274          * Set true if the camera option should be enabled.
275          * If not set or false, and multiple cameras are supported,
276          * the camera option will be disabled.
277          *
278          * If multiple cameras are not supported, this preference
279          * is ignored and the camera option will not be visible.
280          */
281         public boolean enableCamera;
282
283         /**
284          * Set true if the camera option should not be visible, regardless
285          * of hardware limitations.
286          */
287         public boolean hideCamera;
288
289         /**
290          * Set true if the photo flash option should be enabled.
291          * If not set or false, the photo flash option will be
292          * disabled.
293          *
294          * If the hardware does not support multiple flash values,
295          * this preference is ignored and the flash option will
296          * be disabled.  It will not be made invisible in order to
297          * preserve a consistent experience across devices and between
298          * front and back cameras.
299          */
300         public boolean enableFlash;
301
302         /**
303          * Set true if the video flash option should be enabled.
304          * Same disable rules apply as the photo flash option.
305          */
306         public boolean enableTorchFlash;
307
308         /**
309          * Set true if the HDR+ flash option should be enabled.
310          * Same disable rules apply as the photo flash option.
311          */
312         public boolean enableHdrPlusFlash;
313
314         /**
315          * Set true if flash should not be visible, regardless of
316          * hardware limitations.
317          */
318         public boolean hideFlash;
319
320         /**
321          * Set true if the hdr/hdr+ option should be enabled.
322          * If not set or false, the hdr/hdr+ option will be disabled.
323          *
324          * Hdr or hdr+ will be chosen based on hardware limitations,
325          * with hdr+ prefered.
326          *
327          * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
328          * will not be visible.
329          */
330         public boolean enableHdr;
331
332         /**
333          * Set true if hdr/hdr+ should not be visible, regardless of
334          * hardware limitations.
335          */
336         public boolean hideHdr;
337
338         /**
339          * Set true if grid lines should be visible.  Not setting this
340          * causes grid lines to be disabled.  This option is agnostic to
341          * the hardware.
342          */
343         public boolean enableGridLines;
344
345         /**
346          * Set true if grid lines should not be visible.
347          */
348         public boolean hideGridLines;
349
350         /**
351          * Set true if the panorama orientation option should be visible.
352          *
353          * This option is not constrained by hardware limitations.
354          */
355         public boolean enablePanoOrientation;
356
357         public boolean enableExposureCompensation;
358
359         /** Intent UI */
360
361         /**
362          * Set true if the intent ui cancel option should be visible.
363          */
364         public boolean showCancel;
365         /**
366          * Set true if the intent ui done option should be visible.
367          */
368         public boolean showDone;
369         /**
370          * Set true if the intent ui retake option should be visible.
371          */
372         public boolean showRetake;
373         /**
374          * Set true if the intent ui review option should be visible.
375          */
376         public boolean showReview;
377
378         /** Mode options callbacks */
379
380         /**
381          * A {@link com.android.camera.ButtonManager.ButtonCallback}
382          * that will be executed when the camera option is pressed. This
383          * callback can be null.
384          */
385         public ButtonManager.ButtonCallback cameraCallback;
386
387         /**
388          * A {@link com.android.camera.ButtonManager.ButtonCallback}
389          * that will be executed when the flash option is pressed. This
390          * callback can be null.
391          */
392         public ButtonManager.ButtonCallback flashCallback;
393
394         /**
395          * A {@link com.android.camera.ButtonManager.ButtonCallback}
396          * that will be executed when the hdr/hdr+ option is pressed. This
397          * callback can be null.
398          */
399         public ButtonManager.ButtonCallback hdrCallback;
400
401         /**
402          * A {@link com.android.camera.ButtonManager.ButtonCallback}
403          * that will be executed when the grid lines option is pressed. This
404          * callback can be null.
405          */
406         public ButtonManager.ButtonCallback gridLinesCallback;
407
408         /**
409          * A {@link com.android.camera.ButtonManager.ButtonCallback}
410          * that will execute when the panorama orientation option is pressed.
411          * This callback can be null.
412          */
413         public ButtonManager.ButtonCallback panoOrientationCallback;
414
415         /** Intent UI callbacks */
416
417         /**
418          * A {@link android.view.View.OnClickListener} that will execute
419          * when the cancel option is pressed. This callback can be null.
420          */
421         public View.OnClickListener cancelCallback;
422
423         /**
424          * A {@link android.view.View.OnClickListener} that will execute
425          * when the done option is pressed. This callback can be null.
426          */
427         public View.OnClickListener doneCallback;
428
429         /**
430          * A {@link android.view.View.OnClickListener} that will execute
431          * when the retake option is pressed. This callback can be null.
432          */
433         public View.OnClickListener retakeCallback;
434
435         /**
436          * A {@link android.view.View.OnClickListener} that will execute
437          * when the review option is pressed. This callback can be null.
438          */
439         public View.OnClickListener reviewCallback;
440
441         /**
442          * A ExposureCompensationSetCallback that will execute
443          * when an expsosure button is pressed. This callback can be null.
444          */
445         public interface ExposureCompensationSetCallback {
446             public void setExposure(int value);
447         }
448         public ExposureCompensationSetCallback exposureCompensationSetCallback;
449
450         /**
451          * Exposure compensation parameters.
452          */
453         public int minExposureCompensation;
454         public int maxExposureCompensation;
455         public float exposureCompensationStep;
456
457         /**
458          * Whether self-timer is enabled.
459          */
460         public boolean enableSelfTimer = false;
461
462         /**
463          * Whether the option for self-timer should show. If true and
464          * {@link #enableSelfTimer} is false, then the option should be shown
465          * disabled.
466          */
467         public boolean showSelfTimer = false;
468     }
469
470
471     private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
472
473     private final AppController mController;
474     private final boolean mIsCaptureIntent;
475     private final AnimationManager mAnimationManager;
476
477     // Swipe states:
478     private final static int IDLE = 0;
479     private final static int SWIPE_UP = 1;
480     private final static int SWIPE_DOWN = 2;
481     private final static int SWIPE_LEFT = 3;
482     private final static int SWIPE_RIGHT = 4;
483     private boolean mSwipeEnabled = true;
484
485     // Shared Surface Texture properities.
486     private SurfaceTexture mSurface;
487     private int mSurfaceWidth;
488     private int mSurfaceHeight;
489
490     // Touch related measures:
491     private final int mSlop;
492     private final static int SWIPE_TIME_OUT_MS = 500;
493
494     // Mode cover states:
495     private final static int COVER_HIDDEN = 0;
496     private final static int COVER_SHOWN = 1;
497     private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
498     private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
499
500     /**
501      * Preview down-sample rate when taking a screenshot.
502      */
503     private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
504
505     // App level views:
506     private final FrameLayout mCameraRootView;
507     private final ModeTransitionView mModeTransitionView;
508     private final MainActivityLayout mAppRootView;
509     private final ModeListView mModeListView;
510     private final FilmstripLayout mFilmstripLayout;
511     private TextureView mTextureView;
512     private FrameLayout mModuleUI;
513     private ShutterButton mShutterButton;
514     private BottomBar mBottomBar;
515     private ModeOptionsOverlay mModeOptionsOverlay;
516     private IndicatorIconController mIndicatorIconController;
517     private FocusRing mFocusRing;
518     private FrameLayout mTutorialsPlaceHolderWrapper;
519     private StickyBottomCaptureLayout mStickyBottomCaptureLayout;
520     private TextureViewHelper mTextureViewHelper;
521     private final GestureDetector mGestureDetector;
522     private DisplayManager.DisplayListener mDisplayListener;
523     private int mLastRotation;
524     private int mSwipeState = IDLE;
525     private PreviewOverlay mPreviewOverlay;
526     private GridLines mGridLines;
527     private CaptureAnimationOverlay mCaptureOverlay;
528     private PreviewStatusListener mPreviewStatusListener;
529     private int mModeCoverState = COVER_HIDDEN;
530     private final FilmstripBottomPanel mFilmstripBottomControls;
531     private final FilmstripContentPanel mFilmstripPanel;
532     private Runnable mHideCoverRunnable;
533     private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
534             = new View.OnLayoutChangeListener() {
535         @Override
536         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
537                 int oldTop, int oldRight, int oldBottom) {
538             if (mPreviewStatusListener != null) {
539                 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
540                         oldTop, oldRight, oldBottom);
541             }
542         }
543     };
544     private View mModeOptionsToggle;
545     private final RoundedThumbnailView mRoundedThumbnailView;
546     private final CaptureLayoutHelper mCaptureLayoutHelper;
547     private boolean mAccessibilityEnabled;
548     private final View mAccessibilityAffordances;
549
550     private boolean mDisableAllUserInteractions;
551     /**
552      * Provides current preview frame and the controls/overlay from the module that
553      * are shown on top of the preview.
554      */
555     public interface CameraModuleScreenShotProvider {
556         /**
557          * Returns the current preview frame down-sampled using the given down-sample
558          * factor.
559          *
560          * @param downSampleFactor the down sample factor for down sampling the
561          *                         preview frame. (e.g. a down sample factor of
562          *                         2 means to scale down the preview frame to 1/2
563          *                         the width and height.)
564          * @return down-sampled preview frame
565          */
566         public Bitmap getPreviewFrame(int downSampleFactor);
567
568         /**
569          * @return the controls and overlays that are currently showing on top of
570          *         the preview drawn into a bitmap with no scaling applied.
571          */
572         public Bitmap getPreviewOverlayAndControls();
573
574         /**
575          * Returns a bitmap containing the current screenshot.
576          *
577          * @param previewDownSampleFactor the downsample factor applied on the
578          *                                preview frame when taking the screenshot
579          */
580         public Bitmap getScreenShot(int previewDownSampleFactor);
581     }
582
583     /**
584      * This listener gets called when the size of the window (excluding the system
585      * decor such as status bar and nav bar) has changed.
586      */
587     public interface NonDecorWindowSizeChangedListener {
588         public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
589     }
590
591     private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
592             new CameraModuleScreenShotProvider() {
593                 @Override
594                 public Bitmap getPreviewFrame(int downSampleFactor) {
595                     if (mCameraRootView == null || mTextureView == null) {
596                         return null;
597                     }
598                     // Gets the bitmap from the preview TextureView.
599                     Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
600                     return preview;
601                 }
602
603                 @Override
604                 public Bitmap getPreviewOverlayAndControls() {
605                     Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
606                             mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
607                     Canvas canvas = new Canvas(overlays);
608                     mCameraRootView.draw(canvas);
609                     return overlays;
610                 }
611
612                 @Override
613                 public Bitmap getScreenShot(int previewDownSampleFactor) {
614                     Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
615                             mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
616                     Canvas canvas = new Canvas(screenshot);
617                     canvas.drawARGB(255, 0, 0, 0);
618                     Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
619                     if (preview != null) {
620                         canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
621                     }
622                     Bitmap overlay = getPreviewOverlayAndControls();
623                     if (overlay != null) {
624                         canvas.drawBitmap(overlay, 0f, 0f, null);
625                     }
626                     return screenshot;
627                 }
628             };
629
630     private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
631
632     public long getCoverHiddenTime() {
633         return mCoverHiddenTime;
634     }
635
636     /**
637      * This resets the preview to have no applied transform matrix.
638      */
639     public void clearPreviewTransform() {
640         mTextureViewHelper.clearTransform();
641     }
642
643     public void updatePreviewAspectRatio(float aspectRatio) {
644         mTextureViewHelper.updateAspectRatio(aspectRatio);
645     }
646
647     /**
648      * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
649      * its TextureView.  This is necessary to get the expected behavior for the TextureView's
650      * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
651      * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
652      * size).
653      *
654      * b/17286155 - Tracking a fix for this in HardwareLayer.
655      */
656     public void setDefaultBufferSizeToViewDimens() {
657         if (mSurface == null || mTextureView == null) {
658             Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
659             return;
660         }
661         mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
662     }
663
664     /**
665      * Updates the preview matrix without altering it.
666      *
667      * @param matrix
668      * @param aspectRatio the desired aspect ratio for the preview.
669      */
670     public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
671         mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
672     }
673
674     /**
675      * @return the rect that will display the preview.
676      */
677     public RectF getFullscreenRect() {
678         return mTextureViewHelper.getFullscreenRect();
679     }
680
681     /**
682      * This is to support modules that calculate their own transform matrix because
683      * they need to use a transform matrix to rotate the preview.
684      *
685      * @param matrix transform matrix to be set on the TextureView
686      */
687     public void updatePreviewTransform(Matrix matrix) {
688         mTextureViewHelper.updateTransform(matrix);
689     }
690
691     public interface AnimationFinishedListener {
692         public void onAnimationFinished(boolean success);
693     }
694
695     private class MyTouchListener implements View.OnTouchListener {
696         private boolean mScaleStarted = false;
697         @Override
698         public boolean onTouch(View v, MotionEvent event) {
699             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
700                 mScaleStarted = false;
701             } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
702                 mScaleStarted = true;
703             }
704             return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
705         }
706     }
707
708     /**
709      * This gesture listener finds out the direction of the scroll gestures and
710      * sends them to CameraAppUI to do further handling.
711      */
712     private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
713         private MotionEvent mDown;
714
715         @Override
716         public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
717             if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
718                     || mSwipeState != IDLE
719                     || mIsCaptureIntent
720                     || !mSwipeEnabled) {
721                 return false;
722             }
723
724             int deltaX = (int) (ev.getX() - mDown.getX());
725             int deltaY = (int) (ev.getY() - mDown.getY());
726             if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
727                 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
728                     // Calculate the direction of the swipe.
729                     if (deltaX >= Math.abs(deltaY)) {
730                         // Swipe right.
731                         setSwipeState(SWIPE_RIGHT);
732                     } else if (deltaX <= -Math.abs(deltaY)) {
733                         // Swipe left.
734                         setSwipeState(SWIPE_LEFT);
735                     }
736                 }
737             }
738             return true;
739         }
740
741         private void setSwipeState(int swipeState) {
742             mSwipeState = swipeState;
743             // Notify new swipe detected.
744             onSwipeDetected(swipeState);
745         }
746
747         @Override
748         public boolean onDown(MotionEvent ev) {
749             mDown = MotionEvent.obtain(ev);
750             mSwipeState = IDLE;
751             return false;
752         }
753     }
754
755     public CameraAppUI(AppController controller, MainActivityLayout appRootView,
756             boolean isCaptureIntent) {
757         mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
758         mController = controller;
759         mIsCaptureIntent = isCaptureIntent;
760
761         mAppRootView = appRootView;
762         mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
763         mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
764         mModeTransitionView = (ModeTransitionView)
765                 mAppRootView.findViewById(R.id.mode_transition_view);
766         mFilmstripBottomControls = new FilmstripBottomPanel(controller,
767                 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
768         mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
769         mGestureDetector = new GestureDetector(controller.getAndroidContext(),
770                 new MyGestureListener());
771         Resources res = controller.getAndroidContext().getResources();
772         mCaptureLayoutHelper = new CaptureLayoutHelper(
773                 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
774                 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
775                 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
776         mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
777         if (mModeListView != null) {
778             mModeListView.setModeSwitchListener(this);
779             mModeListView.setModeListOpenListener(this);
780             mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
781             mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
782             boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
783                     SettingsManager.SCOPE_GLOBAL,
784                     Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
785             mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
786         } else {
787             Log.e(TAG, "Cannot find mode list in the view hierarchy");
788         }
789         mAnimationManager = new AnimationManager();
790         mRoundedThumbnailView = (RoundedThumbnailView) appRootView.findViewById(R.id.rounded_thumbnail_view);
791         mRoundedThumbnailView.setOnClickListener(new View.OnClickListener() {
792             @Override
793             public void onClick(View v) {
794                 mFilmstripLayout.showFilmstrip();
795             }
796         });
797
798         mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
799         initDisplayListener();
800         mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
801         View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
802         modeListToggle.setOnClickListener(new View.OnClickListener() {
803             @Override
804             public void onClick(View view) {
805                 openModeList();
806             }
807         });
808         View filmstripToggle = mAppRootView.findViewById(
809                 R.id.accessibility_filmstrip_toggle_button);
810         filmstripToggle.setOnClickListener(new View.OnClickListener() {
811             @Override
812             public void onClick(View view) {
813                 showFilmstrip();
814             }
815         });
816     }
817
818
819     /**
820      * Freeze what is currently shown on screen until the next preview frame comes
821      * in.
822      */
823     public void freezeScreenUntilPreviewReady() {
824         Log.v(TAG, "freezeScreenUntilPreviewReady");
825         mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
826                 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
827         mHideCoverRunnable = new Runnable() {
828             @Override
829             public void run() {
830                 mModeTransitionView.hideImageCover();
831             }
832         };
833         mModeCoverState = COVER_SHOWN;
834     }
835
836     /**
837      * Creates a cling for the specific viewer and links the cling to the corresponding
838      * button for layout position.
839      *
840      * @param viewerType defines which viewer the cling is for.
841      */
842     public void setupClingForViewer(int viewerType) {
843         if (viewerType == BottomPanel.VIEWER_REFOCUS) {
844             FrameLayout filmstripContent = (FrameLayout) mAppRootView
845                     .findViewById(R.id.camera_filmstrip_content_layout);
846             if (filmstripContent != null) {
847                 // Creates refocus cling.
848                 LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
849                         .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
850                 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
851                 // Sets instruction text in the cling.
852                 refocusCling.setText(mController.getAndroidContext().getResources()
853                         .getString(R.string.cling_text_for_refocus_editor_button));
854
855                 // Adds cling into view hierarchy.
856                 int clingWidth = mController.getAndroidContext()
857                         .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
858                 filmstripContent.addView(refocusCling, clingWidth,
859                         ViewGroup.LayoutParams.WRAP_CONTENT);
860                 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
861             }
862         }
863     }
864
865     /**
866      * Clears the listeners for the cling and remove it from the view hierarchy.
867      *
868      * @param viewerType defines which viewer the cling is for.
869      */
870     public void clearClingForViewer(int viewerType) {
871         Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
872         if (clingToBeRemoved == null) {
873             // No cling is created for the specific viewer type.
874             return;
875         }
876         mFilmstripBottomControls.clearClingForViewer(viewerType);
877         clingToBeRemoved.setVisibility(View.GONE);
878         mAppRootView.removeView(clingToBeRemoved);
879     }
880
881     /**
882      * Enable or disable swipe gestures. We want to disable them e.g. while we
883      * record a video.
884      */
885     public void setSwipeEnabled(boolean enabled) {
886         mSwipeEnabled = enabled;
887         // TODO: This can be removed once we come up with a new design for handling swipe
888         // on shutter button and mode options. (More details: b/13751653)
889         mAppRootView.setSwipeEnabled(enabled);
890     }
891
892     public void onDestroy() {
893         ((DisplayManager) mController.getAndroidContext()
894                 .getSystemService(Context.DISPLAY_SERVICE))
895                 .unregisterDisplayListener(mDisplayListener);
896     }
897
898     /**
899      * Initializes the display listener to listen to display changes such as
900      * 180-degree rotation change, which will not have an onConfigurationChanged
901      * callback.
902      */
903     private void initDisplayListener() {
904         if (ApiHelper.HAS_DISPLAY_LISTENER) {
905             mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext());
906
907             mDisplayListener = new DisplayManager.DisplayListener() {
908                 @Override
909                 public void onDisplayAdded(int arg0) {
910                     // Do nothing.
911                 }
912
913                 @Override
914                 public void onDisplayChanged(int displayId) {
915                     int rotation = CameraUtil.getDisplayRotation(
916                             mController.getAndroidContext());
917                     if ((rotation - mLastRotation + 360) % 360 == 180
918                             && mPreviewStatusListener != null) {
919                         mPreviewStatusListener.onPreviewFlipped();
920                         mStickyBottomCaptureLayout.requestLayout();
921                         mModeListView.requestLayout();
922                         mTextureView.requestLayout();
923                     }
924                     mLastRotation = rotation;
925                 }
926
927                 @Override
928                 public void onDisplayRemoved(int arg0) {
929                     // Do nothing.
930                 }
931             };
932
933             ((DisplayManager) mController.getAndroidContext()
934                     .getSystemService(Context.DISPLAY_SERVICE))
935                     .registerDisplayListener(mDisplayListener, null);
936         }
937     }
938
939     /**
940      * Redirects touch events to appropriate recipient views based on swipe direction.
941      * More specifically, swipe up and swipe down will be handled by the view that handles
942      * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
943      * to mode list in order to bring up mode list.
944      */
945     private void onSwipeDetected(int swipeState) {
946         if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
947             // TODO: Polish quick switch after this release.
948             // Quick switch between modes.
949             int currentModuleIndex = mController.getCurrentModuleIndex();
950             final int moduleToTransitionTo =
951                     mController.getQuickSwitchToModuleId(currentModuleIndex);
952             if (currentModuleIndex != moduleToTransitionTo) {
953                 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
954                 int shadeColorId = R.color.mode_cover_default_color;
955                 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
956                         mController.getAndroidContext());
957
958                 AnimationFinishedListener listener = new AnimationFinishedListener() {
959                     @Override
960                     public void onAnimationFinished(boolean success) {
961                         if (success) {
962                             mHideCoverRunnable = new Runnable() {
963                                 @Override
964                                 public void run() {
965                                     mModeTransitionView.startPeepHoleAnimation();
966                                 }
967                             };
968                             mModeCoverState = COVER_SHOWN;
969                             // Go to new module when the previous operation is successful.
970                             mController.onModeSelected(moduleToTransitionTo);
971                         }
972                     }
973                 };
974             }
975         } else if (swipeState == SWIPE_LEFT) {
976             // Pass the touch sequence to filmstrip layout.
977             mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
978         } else if (swipeState == SWIPE_RIGHT) {
979             // Pass the touch to mode switcher
980             mAppRootView.redirectTouchEventsTo(mModeListView);
981         }
982     }
983
984     /**
985      * Gets called when activity resumes in preview.
986      */
987     public void resume() {
988         // Show mode theme cover until preview is ready
989         showModeCoverUntilPreviewReady();
990
991         // Hide action bar first since we are in full screen mode first, and
992         // switch the system UI to lights-out mode.
993         mFilmstripPanel.hide();
994
995         // Show UI that is meant to only be used when spoken feedback is
996         // enabled.
997         mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
998         mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
999     }
1000
1001     /**
1002      * @return Whether any spoken feedback accessibility feature is currently
1003      *         enabled.
1004      */
1005     private boolean isSpokenFeedbackAccessibilityEnabled() {
1006         AccessibilityManager accessibilityManager = (AccessibilityManager) mController
1007                 .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1008         List<AccessibilityServiceInfo> infos = accessibilityManager
1009                 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1010         return infos != null && !infos.isEmpty();
1011     }
1012
1013     /**
1014      * Opens the mode list (e.g. because of the menu button being pressed) and
1015      * adapts the rest of the UI.
1016      */
1017     public void openModeList() {
1018         mModeOptionsOverlay.closeModeOptions();
1019         mModeListView.onMenuPressed();
1020     }
1021
1022     /**
1023      * A cover view showing the mode theme color and mode icon will be visible on
1024      * top of preview until preview is ready (i.e. camera preview is started and
1025      * the first frame has been received).
1026      */
1027     private void showModeCoverUntilPreviewReady() {
1028         int modeId = mController.getCurrentModuleIndex();
1029         int colorId = R.color.mode_cover_default_color;;
1030         int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1031         mModeTransitionView.setupModeCover(colorId, iconId);
1032         mHideCoverRunnable = new Runnable() {
1033             @Override
1034             public void run() {
1035                 mModeTransitionView.hideModeCover(null);
1036                 if (!mDisableAllUserInteractions) {
1037                     showShimmyDelayed();
1038                 }
1039             }
1040         };
1041         mModeCoverState = COVER_SHOWN;
1042     }
1043
1044     private void showShimmyDelayed() {
1045         if (!mIsCaptureIntent) {
1046             // Show shimmy in SHIMMY_DELAY_MS
1047             mModeListView.showModeSwitcherHint();
1048         }
1049     }
1050
1051     private void hideModeCover() {
1052         if (mHideCoverRunnable != null) {
1053             mAppRootView.post(mHideCoverRunnable);
1054             mHideCoverRunnable = null;
1055         }
1056         mModeCoverState = COVER_HIDDEN;
1057         if (mCoverHiddenTime < 0) {
1058             mCoverHiddenTime = System.currentTimeMillis();
1059         }
1060     }
1061
1062
1063     public void onPreviewVisiblityChanged(int visibility) {
1064         if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1065             setIndicatorBottomBarWrapperVisible(false);
1066             mAccessibilityAffordances.setVisibility(View.GONE);
1067         } else {
1068             setIndicatorBottomBarWrapperVisible(true);
1069             if (mAccessibilityEnabled) {
1070                 mAccessibilityAffordances.setVisibility(View.VISIBLE);
1071             } else {
1072                 mAccessibilityAffordances.setVisibility(View.GONE);
1073             }
1074         }
1075     }
1076
1077     /**
1078      * Call to stop the preview from being rendered.
1079      */
1080     public void pausePreviewRendering() {
1081         mTextureView.setVisibility(View.INVISIBLE);
1082     }
1083
1084     /**
1085      * Call to begin rendering the preview again.
1086      */
1087     public void resumePreviewRendering() {
1088         mTextureView.setVisibility(View.VISIBLE);
1089     }
1090
1091     /**
1092      * Returns the transform associated with the preview view.
1093      *
1094      * @param m the Matrix in which to copy the current transform.
1095      * @return The specified matrix if not null or a new Matrix instance
1096      *         otherwise.
1097      */
1098     public Matrix getPreviewTransform(Matrix m) {
1099         return mTextureView.getTransform(m);
1100     }
1101
1102     @Override
1103     public void onOpenFullScreen() {
1104         // Do nothing.
1105     }
1106
1107     @Override
1108     public void onModeListOpenProgress(float progress) {
1109         progress = 1 - progress;
1110         float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1111         mModeOptionsToggle.setAlpha(interpolatedProgress);
1112         // Change shutter button alpha linearly based on the mode list open progress:
1113         // set the alpha to disabled alpha when list is fully open, to enabled alpha
1114         // when the list is fully closed.
1115         mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1116                 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1117     }
1118
1119     @Override
1120     public void onModeListClosed() {
1121         // Make sure the alpha on mode options ellipse is reset when mode drawer
1122         // is closed.
1123         mModeOptionsToggle.setAlpha(1f);
1124         mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1125     }
1126
1127     /**
1128      * Called when the back key is pressed.
1129      *
1130      * @return Whether the UI responded to the key event.
1131      */
1132     public boolean onBackPressed() {
1133         if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1134             return mFilmstripLayout.onBackPressed();
1135         } else {
1136             return mModeListView.onBackPressed();
1137         }
1138     }
1139
1140     /**
1141      * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1142      * listens to SurfaceTexture changes. In addition, listeners are set on
1143      * dependent app ui elements.
1144      *
1145      * @param previewStatusListener the listener that gets notified when SurfaceTexture
1146      *                              changes
1147      */
1148     public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1149         mPreviewStatusListener = previewStatusListener;
1150         if (mPreviewStatusListener != null) {
1151             onPreviewListenerChanged();
1152         }
1153     }
1154
1155     /**
1156      * When the PreviewStatusListener changes, listeners need to be
1157      * set on the following app ui elements:
1158      * {@link com.android.camera.ui.PreviewOverlay},
1159      * {@link com.android.camera.ui.BottomBar},
1160      * {@link com.android.camera.ui.IndicatorIconController}.
1161      */
1162     private void onPreviewListenerChanged() {
1163         // Set a listener for recognizing preview gestures.
1164         GestureDetector.OnGestureListener gestureListener
1165             = mPreviewStatusListener.getGestureListener();
1166         if (gestureListener != null) {
1167             mPreviewOverlay.setGestureListener(gestureListener);
1168         }
1169         View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1170         if (touchListener != null) {
1171             mPreviewOverlay.setTouchListener(touchListener);
1172         }
1173
1174         mTextureViewHelper.setAutoAdjustTransform(
1175                 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1176     }
1177
1178     /**
1179      * This method should be called in onCameraOpened.  It defines CameraAppUI
1180      * specific changes that depend on the camera or camera settings.
1181      */
1182     public void onChangeCamera() {
1183         ModuleController moduleController = mController.getCurrentModuleController();
1184         applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1185
1186         if (mIndicatorIconController != null) {
1187             // Sync the settings state with the indicator state.
1188             mIndicatorIconController.syncIndicators();
1189         }
1190     }
1191
1192     /**
1193      * Adds a listener to receive callbacks when preview area changes.
1194      */
1195     public void addPreviewAreaChangedListener(
1196             PreviewStatusListener.PreviewAreaChangedListener listener) {
1197         mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1198     }
1199
1200     /**
1201      * Removes a listener that receives callbacks when preview area changes.
1202      */
1203     public void removePreviewAreaChangedListener(
1204             PreviewStatusListener.PreviewAreaChangedListener listener) {
1205         mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1206     }
1207
1208     /**
1209      * This inflates generic_module layout, which contains all the shared views across
1210      * modules. Then each module inflates their own views in the given view group. For
1211      * now, this is called every time switching from a not-yet-refactored module to a
1212      * refactored module. In the future, this should only need to be done once per app
1213      * start.
1214      */
1215     public void prepareModuleUI() {
1216         mController.getSettingsManager().addListener(this);
1217         mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1218         mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1219         mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1220                 mController.getCameraProvider());
1221         mTextureViewHelper.setSurfaceTextureListener(this);
1222         mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1223
1224         mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1225         int unpressedColor = mController.getAndroidContext().getResources()
1226             .getColor(R.color.bottombar_unpressed);
1227         setBottomBarColor(unpressedColor);
1228         updateModeSpecificUIColors();
1229
1230         mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1231
1232         mModeOptionsOverlay
1233             = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1234
1235         // Sets the visibility of the bottom bar and the mode options.
1236         resetBottomControls(mController.getCurrentModuleController(),
1237             mController.getCurrentModuleIndex());
1238         mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1239
1240         mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1241         addShutterListener(mController.getCurrentModuleController());
1242         addShutterListener(mModeOptionsOverlay);
1243         addShutterListener(this);
1244
1245         mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1246         mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1247
1248         mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1249         mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1250         mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1251
1252         mCaptureOverlay = (CaptureAnimationOverlay)
1253                 mCameraRootView.findViewById(R.id.capture_overlay);
1254         mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1255         mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1256
1257         if (mIndicatorIconController == null) {
1258             mIndicatorIconController =
1259                 new IndicatorIconController(mController, mAppRootView);
1260         }
1261
1262         mController.getButtonManager().load(mCameraRootView);
1263         mController.getButtonManager().setListener(mIndicatorIconController);
1264         mController.getSettingsManager().addListener(mIndicatorIconController);
1265
1266         mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1267         mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1268         mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1269                 .findViewById(R.id.tutorials_placeholder_wrapper);
1270         mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1271                 .findViewById(R.id.sticky_bottom_capture_layout);
1272         mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1273
1274         mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1275         mTextureViewHelper.addAspectRatioChangedListener(
1276                 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1277                     @Override
1278                     public void onPreviewAspectRatioChanged(float aspectRatio) {
1279                         mModeOptionsOverlay.requestLayout();
1280                         mBottomBar.requestLayout();
1281                     }
1282                 }
1283         );
1284     }
1285
1286     /**
1287      * Called indirectly from each module in their initialization to get a view group
1288      * to inflate the module specific views in.
1289      *
1290      * @return a view group for modules to attach views to
1291      */
1292     public FrameLayout getModuleRootView() {
1293         // TODO: Change it to mModuleUI when refactor is done
1294         return mCameraRootView;
1295     }
1296
1297     /**
1298      * Remove all the module specific views.
1299      */
1300     public void clearModuleUI() {
1301         if (mModuleUI != null) {
1302             mModuleUI.removeAllViews();
1303         }
1304         removeShutterListener(mController.getCurrentModuleController());
1305         mTutorialsPlaceHolderWrapper.removeAllViews();
1306         mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1307
1308         setShutterButtonEnabled(true);
1309         mPreviewStatusListener = null;
1310         mPreviewOverlay.reset();
1311
1312         Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1313         mFocusRing.stopFocusAnimations();
1314     }
1315
1316     /**
1317      * Gets called when preview is ready to start. It sets up one shot preview callback
1318      * in order to receive a callback when the preview frame is available, so that
1319      * the preview cover can be hidden to reveal preview.
1320      *
1321      * An alternative for getting the timing to hide preview cover is through
1322      * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1323      * which is less accurate but therefore is the fallback for modules that manage
1324      * their own preview callbacks (as setting one preview callback will override
1325      * any other installed preview callbacks), or use camera2 API.
1326      */
1327     public void onPreviewReadyToStart() {
1328         if (mModeCoverState == COVER_SHOWN) {
1329             mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1330             mController.setupOneShotPreviewListener();
1331         }
1332     }
1333
1334     /**
1335      * Gets called when preview is started.
1336      */
1337     public void onPreviewStarted() {
1338         Log.v(TAG, "onPreviewStarted");
1339         if (mModeCoverState == COVER_SHOWN) {
1340             mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1341         }
1342         enableModeOptions();
1343     }
1344
1345     /**
1346      * Gets notified when next preview frame comes in.
1347      */
1348     public void onNewPreviewFrame() {
1349         Log.v(TAG, "onNewPreviewFrame");
1350         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1351         hideModeCover();
1352     }
1353
1354     @Override
1355     public void onShutterButtonClick() {
1356         /*
1357          * Set the mode options toggle unclickable, generally
1358          * throughout the app, whenever the shutter button is clicked.
1359          *
1360          * This could be done in the OnShutterButtonListener of the
1361          * ModeOptionsOverlay, but since it is very important that we
1362          * can clearly see when the toggle becomes clickable again,
1363          * keep all of that logic at this level.
1364          */
1365         // disableModeOptions();
1366     }
1367
1368     @Override
1369     public void onShutterCoordinate(TouchCoordinate coord) {
1370         // Do nothing.
1371     }
1372
1373     @Override
1374     public void onShutterButtonFocus(boolean pressed) {
1375         // noop
1376     }
1377
1378     @Override
1379     public void onShutterButtonLongPressed() {
1380         // noop
1381     }
1382
1383     /**
1384      * Set the mode options toggle clickable.
1385      */
1386     public void enableModeOptions() {
1387         /*
1388          * For modules using camera 1 api, this gets called in
1389          * onSurfaceTextureUpdated whenever the preview gets stopped and
1390          * started after each capture.  This also takes care of the
1391          * case where the mode options might be unclickable when we
1392          * switch modes
1393          *
1394          * For modules using camera 2 api, they're required to call this
1395          * method when a capture is "completed".  Unfortunately this differs
1396          * per module implementation.
1397          */
1398         if (!mDisableAllUserInteractions) {
1399             mModeOptionsOverlay.setToggleClickable(true);
1400         }
1401     }
1402
1403     /**
1404      * Set the mode options toggle not clickable.
1405      */
1406     public void disableModeOptions() {
1407         mModeOptionsOverlay.setToggleClickable(false);
1408     }
1409
1410     public void setDisableAllUserInteractions(boolean disable) {
1411         if (disable) {
1412             disableModeOptions();
1413             setShutterButtonEnabled(false);
1414             setSwipeEnabled(false);
1415             mModeListView.hideAnimated();
1416         } else {
1417             enableModeOptions();
1418             setShutterButtonEnabled(true);
1419             setSwipeEnabled(true);
1420         }
1421         mDisableAllUserInteractions = disable;
1422     }
1423
1424     @Override
1425     public void onModeButtonPressed(int modeIndex) {
1426         // TODO: Make CameraActivity listen to ModeListView's events.
1427         int pressedModuleId = mController.getModuleId(modeIndex);
1428         int currentModuleId = mController.getCurrentModuleIndex();
1429         if (pressedModuleId != currentModuleId) {
1430             hideCaptureIndicator();
1431         }
1432     }
1433
1434     /**
1435      * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1436      *
1437      * @param modeIndex mode index of the selected mode
1438      */
1439     @Override
1440     public void onModeSelected(int modeIndex) {
1441         mHideCoverRunnable = new Runnable() {
1442             @Override
1443             public void run() {
1444                 mModeListView.startModeSelectionAnimation();
1445             }
1446         };
1447         mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1448         mModeCoverState = COVER_SHOWN;
1449
1450         int lastIndex = mController.getCurrentModuleIndex();
1451         // Actual mode teardown / new mode initialization happens here
1452         mController.onModeSelected(modeIndex);
1453         int currentIndex = mController.getCurrentModuleIndex();
1454
1455         if (lastIndex == currentIndex) {
1456             hideModeCover();
1457         }
1458
1459         updateModeSpecificUIColors();
1460     }
1461
1462     private void updateModeSpecificUIColors() {
1463         setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1464     }
1465
1466     @Override
1467     public void onSettingsSelected() {
1468         mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1469                                              Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1470         mModeListView.setShouldShowSettingsCling(false);
1471         mController.onSettingsSelected();
1472     }
1473
1474     @Override
1475     public int getCurrentModeIndex() {
1476         return mController.getCurrentModuleIndex();
1477     }
1478
1479     /********************** Capture animation **********************/
1480     /* TODO: This session is subject to UX changes. In addition to the generic
1481        flash animation and post capture animation, consider designating a parameter
1482        for specifying the type of animation, as well as an animation finished listener
1483        so that modules can have more knowledge of the status of the animation. */
1484
1485     /**
1486      * Starts the capture indicator pop-out animation.
1487      *
1488      * @param accessibilityString An accessibility String to be announced during the peek animation.
1489      */
1490     public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1491         if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1492             return;
1493         }
1494         mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1495     }
1496
1497     /**
1498      * Updates the thumbnail image in the capture indicator.
1499      *
1500      * @param thumbnailBitmap The thumbnail image to be shown.
1501      */
1502     public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
1503         mRoundedThumbnailView.setThumbnail(thumbnailBitmap);
1504         mRoundedThumbnailView.setRotation(rotation);
1505     }
1506
1507     /**
1508      * Hides the capture indicator.
1509      */
1510     public void hideCaptureIndicator() {
1511         mRoundedThumbnailView.hideThumbnail();
1512     }
1513
1514     /**
1515      * Starts the flash animation.
1516      */
1517     public void startFlashAnimation(boolean shortFlash) {
1518         mCaptureOverlay.startFlashAnimation(shortFlash);
1519     }
1520
1521     /**
1522      * Cancels the pre-capture animation.
1523      */
1524     public void cancelPreCaptureAnimation() {
1525         mAnimationManager.cancelAnimations();
1526     }
1527
1528     /**
1529      * Cancels the post-capture animation.
1530      */
1531     public void cancelPostCaptureAnimation() {
1532         mAnimationManager.cancelAnimations();
1533     }
1534
1535     public FilmstripContentPanel getFilmstripContentPanel() {
1536         return mFilmstripPanel;
1537     }
1538
1539     /**
1540      * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1541      * bottom of the filmstrip.
1542      */
1543     public BottomPanel getFilmstripBottomControls() {
1544         return mFilmstripBottomControls;
1545     }
1546
1547     public void showBottomControls() {
1548         mFilmstripBottomControls.show();
1549     }
1550
1551     public void hideBottomControls() {
1552         mFilmstripBottomControls.hide();
1553     }
1554
1555     /**
1556      * @param listener The listener for bottom controls.
1557      */
1558     public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1559         mFilmstripBottomControls.setListener(listener);
1560     }
1561
1562     /***************************SurfaceTexture Api and Listener*********************************/
1563
1564     /**
1565      * Return the shared surface texture.
1566      */
1567     public SurfaceTexture getSurfaceTexture() {
1568         return mSurface;
1569     }
1570
1571     /**
1572      * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1573      */
1574     public int getSurfaceWidth() {
1575         return mSurfaceWidth;
1576     }
1577
1578     /**
1579      * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1580      */
1581     public int getSurfaceHeight() {
1582         return mSurfaceHeight;
1583     }
1584
1585     @Override
1586     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1587         mSurface = surface;
1588         mSurfaceWidth = width;
1589         mSurfaceHeight = height;
1590         Log.v(TAG, "SurfaceTexture is available");
1591         if (mPreviewStatusListener != null) {
1592             mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1593         }
1594         enableModeOptions();
1595     }
1596
1597     @Override
1598     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1599         mSurface = surface;
1600         mSurfaceWidth = width;
1601         mSurfaceHeight = height;
1602         if (mPreviewStatusListener != null) {
1603             mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1604         }
1605     }
1606
1607     @Override
1608     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1609         mSurface = null;
1610         Log.v(TAG, "SurfaceTexture is destroyed");
1611         if (mPreviewStatusListener != null) {
1612             return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1613         }
1614         return false;
1615     }
1616
1617     @Override
1618     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1619         mSurface = surface;
1620         if (mPreviewStatusListener != null) {
1621             mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1622         }
1623         if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1624             Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1625             CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1626             hideModeCover();
1627         }
1628     }
1629
1630     /****************************Grid lines api ******************************/
1631
1632     /**
1633      * Show a set of evenly spaced lines over the preview.  The number
1634      * of lines horizontally and vertically is determined by
1635      * {@link com.android.camera.ui.GridLines}.
1636      */
1637     public void showGridLines() {
1638         if (mGridLines != null) {
1639             mGridLines.setVisibility(View.VISIBLE);
1640         }
1641     }
1642
1643     /**
1644      * Hide the set of evenly spaced grid lines overlaying the preview.
1645      */
1646     public void hideGridLines() {
1647         if (mGridLines != null) {
1648             mGridLines.setVisibility(View.INVISIBLE);
1649         }
1650     }
1651
1652     /**
1653      * Return a callback which shows or hide the preview grid lines
1654      * depending on whether the grid lines setting is set on.
1655      */
1656     public ButtonManager.ButtonCallback getGridLinesCallback() {
1657         return new ButtonManager.ButtonCallback() {
1658             @Override
1659             public void onStateChanged(int state) {
1660                 if (!mController.isPaused()) {
1661                     if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1662                         showGridLines();
1663                     } else {
1664                         hideGridLines();
1665                     }
1666                 }
1667             }
1668         };
1669     }
1670
1671     /***************************Mode options api *****************************/
1672
1673     /**
1674      * Set the mode options visible.
1675      */
1676     public void showModeOptions() {
1677         /* Make mode options clickable. */
1678         enableModeOptions();
1679         mModeOptionsOverlay.setVisibility(View.VISIBLE);
1680     }
1681
1682     /**
1683      * Set the mode options invisible.  This is necessary for modes
1684      * that don't show a bottom bar for the capture UI.
1685      */
1686     public void hideModeOptions() {
1687         mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1688     }
1689
1690     /****************************Bottom bar api ******************************/
1691
1692     /**
1693      * Sets up the bottom bar and mode options with the correct
1694      * shutter button and visibility based on the current module.
1695      */
1696     public void resetBottomControls(ModuleController module, int moduleIndex) {
1697         if (areBottomControlsUsed(module)) {
1698             setBottomBarShutterIcon(moduleIndex);
1699             mCaptureLayoutHelper.setShowBottomBar(true);
1700         } else {
1701             mCaptureLayoutHelper.setShowBottomBar(false);
1702         }
1703     }
1704
1705     /**
1706      * Show or hide the mode options and bottom bar, based on
1707      * whether the current module is using the bottom bar.  Returns
1708      * whether the mode options and bottom bar are used.
1709      */
1710     private boolean areBottomControlsUsed(ModuleController module) {
1711         if (module.isUsingBottomBar()) {
1712             showBottomBar();
1713             showModeOptions();
1714             return true;
1715         } else {
1716             hideBottomBar();
1717             hideModeOptions();
1718             return false;
1719         }
1720     }
1721
1722     /**
1723      * Set the bottom bar visible.
1724      */
1725     public void showBottomBar() {
1726         mBottomBar.setVisibility(View.VISIBLE);
1727     }
1728
1729     /**
1730      * Set the bottom bar invisible.
1731      */
1732     public void hideBottomBar() {
1733         mBottomBar.setVisibility(View.INVISIBLE);
1734     }
1735
1736     /**
1737      * Sets the color of the bottom bar.
1738      */
1739     public void setBottomBarColor(int colorId) {
1740         mBottomBar.setBackgroundColor(colorId);
1741     }
1742
1743     /**
1744      * Sets the pressed color of the bottom bar for a camera mode index.
1745      */
1746     public void setBottomBarColorsForModeIndex(int index) {
1747         mBottomBar.setColorsForModeIndex(index);
1748     }
1749
1750     /**
1751      * Sets the shutter button icon on the bottom bar, based on
1752      * the mode index.
1753      */
1754     public void setBottomBarShutterIcon(int modeIndex) {
1755         int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1756             mController.getAndroidContext());
1757         mBottomBar.setShutterButtonIcon(shutterIconId);
1758     }
1759
1760     public void animateBottomBarToVideoStop(int shutterIconId) {
1761         mBottomBar.animateToVideoStop(shutterIconId);
1762     }
1763
1764     public void animateBottomBarToFullSize(int shutterIconId) {
1765         mBottomBar.animateToFullSize(shutterIconId);
1766     }
1767
1768     public void setShutterButtonEnabled(final boolean enabled) {
1769         if (!mDisableAllUserInteractions) {
1770             mBottomBar.post(new Runnable() {
1771                 @Override
1772                 public void run() {
1773                     mBottomBar.setShutterButtonEnabled(enabled);
1774                 }
1775             });
1776         }
1777     }
1778
1779     public void setShutterButtonImportantToA11y(boolean important) {
1780         mBottomBar.setShutterButtonImportantToA11y(important);
1781     }
1782
1783     public boolean isShutterButtonEnabled() {
1784         return mBottomBar.isShutterButtonEnabled();
1785     }
1786
1787     public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1788         mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1789     }
1790
1791     /**
1792      * Set the visibility of the bottom bar.
1793      */
1794     // TODO: needed for when panorama is managed by the generic module ui.
1795     public void setBottomBarVisible(boolean visible) {
1796         mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1797     }
1798
1799     /**
1800      * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1801      */
1802     public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1803         mShutterButton.addOnShutterButtonListener(listener);
1804     }
1805
1806     /**
1807      * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1808      */
1809     public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1810         mShutterButton.removeOnShutterButtonListener(listener);
1811     }
1812
1813     /**
1814      * Performs a transition to the capture layout of the bottom bar.
1815      */
1816     public void transitionToCapture() {
1817         ModuleController moduleController = mController.getCurrentModuleController();
1818         applyModuleSpecs(moduleController.getHardwareSpec(),
1819             moduleController.getBottomBarSpec());
1820         mBottomBar.transitionToCapture();
1821     }
1822
1823     /**
1824      * Displays the Cancel button instead of the capture button.
1825      */
1826     public void transitionToCancel() {
1827         ModuleController moduleController = mController.getCurrentModuleController();
1828         applyModuleSpecs(moduleController.getHardwareSpec(),
1829                 moduleController.getBottomBarSpec());
1830         mBottomBar.transitionToCancel();
1831     }
1832
1833     /**
1834      * Performs a transition to the global intent layout.
1835      */
1836     public void transitionToIntentCaptureLayout() {
1837         ModuleController moduleController = mController.getCurrentModuleController();
1838         applyModuleSpecs(moduleController.getHardwareSpec(),
1839             moduleController.getBottomBarSpec());
1840         mBottomBar.transitionToIntentCaptureLayout();
1841     }
1842
1843     /**
1844      * Performs a transition to the global intent review layout.
1845      */
1846     public void transitionToIntentReviewLayout() {
1847         ModuleController moduleController = mController.getCurrentModuleController();
1848         applyModuleSpecs(moduleController.getHardwareSpec(),
1849             moduleController.getBottomBarSpec());
1850         mBottomBar.transitionToIntentReviewLayout();
1851     }
1852
1853     /**
1854      * @return whether UI is in intent review mode
1855      */
1856     public boolean isInIntentReview() {
1857         return mBottomBar.isInIntentReview();
1858     }
1859
1860     @Override
1861     public void onSettingChanged(SettingsManager settingsManager, String key) {
1862         // Update the mode options based on the hardware spec,
1863         // when hdr changes to prevent flash from getting out of sync.
1864         if (key.equals(Keys.KEY_CAMERA_HDR)) {
1865             ModuleController moduleController = mController.getCurrentModuleController();
1866             applyModuleSpecs(moduleController.getHardwareSpec(),
1867                              moduleController.getBottomBarSpec());
1868         }
1869     }
1870
1871     /**
1872      * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1873      * to the bottom bar mode options based on limitations from a
1874      * {@link com.android.camera.hardware.HardwareSpec}.
1875      *
1876      * Options not supported by the hardware are either hidden
1877      * or disabled, depending on the option.
1878      *
1879      * Otherwise, the option is fully enabled and clickable.
1880      */
1881     public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1882            final BottomBarUISpec bottomBarSpec) {
1883         if (hardwareSpec == null || bottomBarSpec == null) {
1884             return;
1885         }
1886
1887         ButtonManager buttonManager = mController.getButtonManager();
1888         SettingsManager settingsManager = mController.getSettingsManager();
1889
1890         buttonManager.setToInitialState();
1891
1892         /** Standard mode options */
1893         if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1894                 hardwareSpec.isFrontCameraSupported()) {
1895             if (bottomBarSpec.enableCamera) {
1896                 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1897                         bottomBarSpec.cameraCallback);
1898             } else {
1899                 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1900             }
1901         } else {
1902             // Hide camera icon if front camera not available.
1903             buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1904         }
1905
1906         boolean flashBackCamera = mController.getSettingsManager().getBoolean(
1907             SettingsManager.SCOPE_GLOBAL, Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA);
1908         if (bottomBarSpec.hideFlash || !flashBackCamera) {
1909             // Hide both flash and torch button in flash disable logic
1910             buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1911             buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1912         } else {
1913             if (hardwareSpec.isFlashSupported()) {
1914                 if (bottomBarSpec.enableFlash) {
1915                     buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1916                         bottomBarSpec.flashCallback);
1917                 } else if (bottomBarSpec.enableTorchFlash) {
1918                     buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1919                         bottomBarSpec.flashCallback);
1920                 } else if (bottomBarSpec.enableHdrPlusFlash) {
1921                     buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1922                         bottomBarSpec.flashCallback);
1923                 } else {
1924                     // Hide both flash and torch button in flash disable logic
1925                     buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1926                     buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1927                 }
1928             } else {
1929                 // Disable both flash and torch icon if not supported
1930                 // by the chosen camera hardware.
1931                 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1932                 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1933             }
1934         }
1935
1936         if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1937             // Force hide hdr or hdr plus icon.
1938             buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1939         } else {
1940             if (hardwareSpec.isHdrPlusSupported()) {
1941                 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1942                                                                        mController.getModuleScope())) {
1943                     buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1944                             bottomBarSpec.hdrCallback);
1945                 } else {
1946                     buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1947                 }
1948             } else if (hardwareSpec.isHdrSupported()) {
1949                 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1950                                                                        mController.getModuleScope())) {
1951                     buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1952                             bottomBarSpec.hdrCallback);
1953                 } else {
1954                     buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1955                 }
1956             } else {
1957                 // Hide hdr plus or hdr icon if neither are supported.
1958                 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1959             }
1960         }
1961
1962         if (bottomBarSpec.hideGridLines) {
1963             // Force hide grid lines icon.
1964             buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1965             hideGridLines();
1966         } else {
1967             if (bottomBarSpec.enableGridLines) {
1968                 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1969                         bottomBarSpec.gridLinesCallback != null ?
1970                                 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1971                 );
1972             } else {
1973                 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1974                 hideGridLines();
1975             }
1976         }
1977
1978         if (bottomBarSpec.enableSelfTimer) {
1979             buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
1980         } else {
1981             if (bottomBarSpec.showSelfTimer) {
1982                 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
1983             } else {
1984                 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
1985             }
1986         }
1987
1988         if (bottomBarSpec.enablePanoOrientation
1989                 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
1990             buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
1991         }
1992
1993         boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
1994             !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
1995             mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
1996                         Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
1997         if (enableExposureCompensation) {
1998             buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
1999             buttonManager.setExposureCompensationParameters(
2000                 bottomBarSpec.minExposureCompensation,
2001                 bottomBarSpec.maxExposureCompensation,
2002                 bottomBarSpec.exposureCompensationStep);
2003
2004             buttonManager.setExposureCompensationCallback(
2005                     bottomBarSpec.exposureCompensationSetCallback);
2006             buttonManager.updateExposureButtons();
2007         } else {
2008             buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2009             buttonManager.setExposureCompensationCallback(null);
2010         }
2011
2012         /** Intent UI */
2013         if (bottomBarSpec.showCancel) {
2014             buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2015                     bottomBarSpec.cancelCallback);
2016         }
2017         if (bottomBarSpec.showDone) {
2018             buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2019                     bottomBarSpec.doneCallback);
2020         }
2021         if (bottomBarSpec.showRetake) {
2022             buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2023                     bottomBarSpec.retakeCallback);
2024         }
2025         if (bottomBarSpec.showReview) {
2026             buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2027                     bottomBarSpec.reviewCallback,
2028                     R.drawable.ic_play);
2029         }
2030     }
2031
2032     /**
2033      * Shows the given tutorial on the screen.
2034      */
2035     public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2036         tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2037     }
2038
2039     /***************************Filmstrip api *****************************/
2040
2041     public void showFilmstrip() {
2042         mModeListView.onBackPressed();
2043         mFilmstripLayout.showFilmstrip();
2044     }
2045
2046     public void hideFilmstrip() {
2047         mFilmstripLayout.hideFilmstrip();
2048     }
2049 }