OSDN Git Service

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