OSDN Git Service

add isFlashSUpported() to OneCameraCharacteristics,
[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.Matrix;
24 import android.graphics.RectF;
25 import android.graphics.SurfaceTexture;
26 import android.hardware.display.DisplayManager;
27 import android.util.CameraPerformanceTracker;
28 import android.view.GestureDetector;
29 import android.view.LayoutInflater;
30 import android.view.MotionEvent;
31 import android.view.TextureView;
32 import android.view.View;
33 import android.view.ViewConfiguration;
34 import android.view.ViewGroup;
35 import android.view.accessibility.AccessibilityManager;
36 import android.widget.FrameLayout;
37
38 import com.android.camera.AnimationManager;
39 import com.android.camera.ButtonManager;
40 import com.android.camera.CaptureLayoutHelper;
41 import com.android.camera.ShutterButton;
42 import com.android.camera.TextureViewHelper;
43 import com.android.camera.debug.Log;
44 import com.android.camera.filmstrip.FilmstripContentPanel;
45 import com.android.camera.hardware.HardwareSpec;
46 import com.android.camera.module.ModuleController;
47 import com.android.camera.settings.Keys;
48 import com.android.camera.settings.SettingsManager;
49 import com.android.camera.ui.AbstractTutorialOverlay;
50 import com.android.camera.ui.BottomBar;
51 import com.android.camera.ui.CaptureAnimationOverlay;
52 import com.android.camera.ui.GridLines;
53 import com.android.camera.ui.MainActivityLayout;
54 import com.android.camera.ui.ModeListView;
55 import com.android.camera.ui.ModeTransitionView;
56 import com.android.camera.ui.PreviewOverlay;
57 import com.android.camera.ui.PreviewStatusListener;
58 import com.android.camera.ui.StickyBottomCaptureLayout;
59 import com.android.camera.ui.TouchCoordinate;
60 import com.android.camera.ui.focus.FocusRing;
61 import com.android.camera.util.AndroidServices;
62 import com.android.camera.util.ApiHelper;
63 import com.android.camera.util.CameraUtil;
64 import com.android.camera.util.Gusterpolator;
65 import com.android.camera.util.PhotoSphereHelper;
66 import com.android.camera.widget.Cling;
67 import com.android.camera.widget.FilmstripLayout;
68 import com.android.camera.widget.IndicatorIconController;
69 import com.android.camera.widget.ModeOptionsOverlay;
70 import com.android.camera.widget.RoundedThumbnailView;
71 import com.android.camera2.R;
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 = AndroidServices.instance().provideLayoutInflater();
849                 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
850                 // Sets instruction text in the cling.
851                 refocusCling.setText(mController.getAndroidContext().getResources()
852                         .getString(R.string.cling_text_for_refocus_editor_button));
853
854                 // Adds cling into view hierarchy.
855                 int clingWidth = mController.getAndroidContext()
856                         .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
857                 filmstripContent.addView(refocusCling, clingWidth,
858                         ViewGroup.LayoutParams.WRAP_CONTENT);
859                 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
860             }
861         }
862     }
863
864     /**
865      * Clears the listeners for the cling and remove it from the view hierarchy.
866      *
867      * @param viewerType defines which viewer the cling is for.
868      */
869     public void clearClingForViewer(int viewerType) {
870         Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
871         if (clingToBeRemoved == null) {
872             // No cling is created for the specific viewer type.
873             return;
874         }
875         mFilmstripBottomControls.clearClingForViewer(viewerType);
876         clingToBeRemoved.setVisibility(View.GONE);
877         mAppRootView.removeView(clingToBeRemoved);
878     }
879
880     /**
881      * Enable or disable swipe gestures. We want to disable them e.g. while we
882      * record a video.
883      */
884     public void setSwipeEnabled(boolean enabled) {
885         mSwipeEnabled = enabled;
886         // TODO: This can be removed once we come up with a new design for handling swipe
887         // on shutter button and mode options. (More details: b/13751653)
888         mAppRootView.setSwipeEnabled(enabled);
889     }
890
891     public void onDestroy() {
892         AndroidServices.instance().provideDisplayManager()
893                 .unregisterDisplayListener(mDisplayListener);
894     }
895
896     /**
897      * Initializes the display listener to listen to display changes such as
898      * 180-degree rotation change, which will not have an onConfigurationChanged
899      * callback.
900      */
901     private void initDisplayListener() {
902         if (ApiHelper.HAS_DISPLAY_LISTENER) {
903             mLastRotation = CameraUtil.getDisplayRotation();
904
905             mDisplayListener = new DisplayManager.DisplayListener() {
906                 @Override
907                 public void onDisplayAdded(int arg0) {
908                     // Do nothing.
909                 }
910
911                 @Override
912                 public void onDisplayChanged(int displayId) {
913                     int rotation = CameraUtil.getDisplayRotation(
914                     );
915                     if ((rotation - mLastRotation + 360) % 360 == 180
916                             && mPreviewStatusListener != null) {
917                         mPreviewStatusListener.onPreviewFlipped();
918                         mStickyBottomCaptureLayout.requestLayout();
919                         mModeListView.requestLayout();
920                         mTextureView.requestLayout();
921                     }
922                     mLastRotation = rotation;
923                 }
924
925                 @Override
926                 public void onDisplayRemoved(int arg0) {
927                     // Do nothing.
928                 }
929             };
930
931             AndroidServices.instance().provideDisplayManager()
932                   .registerDisplayListener(mDisplayListener, null);
933         }
934     }
935
936     /**
937      * Redirects touch events to appropriate recipient views based on swipe direction.
938      * More specifically, swipe up and swipe down will be handled by the view that handles
939      * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
940      * to mode list in order to bring up mode list.
941      */
942     private void onSwipeDetected(int swipeState) {
943         if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
944             // TODO: Polish quick switch after this release.
945             // Quick switch between modes.
946             int currentModuleIndex = mController.getCurrentModuleIndex();
947             final int moduleToTransitionTo =
948                     mController.getQuickSwitchToModuleId(currentModuleIndex);
949             if (currentModuleIndex != moduleToTransitionTo) {
950                 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
951                 int shadeColorId = R.color.mode_cover_default_color;
952                 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
953                         mController.getAndroidContext());
954
955                 AnimationFinishedListener listener = new AnimationFinishedListener() {
956                     @Override
957                     public void onAnimationFinished(boolean success) {
958                         if (success) {
959                             mHideCoverRunnable = new Runnable() {
960                                 @Override
961                                 public void run() {
962                                     mModeTransitionView.startPeepHoleAnimation();
963                                 }
964                             };
965                             mModeCoverState = COVER_SHOWN;
966                             // Go to new module when the previous operation is successful.
967                             mController.onModeSelected(moduleToTransitionTo);
968                         }
969                     }
970                 };
971             }
972         } else if (swipeState == SWIPE_LEFT) {
973             // Pass the touch sequence to filmstrip layout.
974             mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
975         } else if (swipeState == SWIPE_RIGHT) {
976             // Pass the touch to mode switcher
977             mAppRootView.redirectTouchEventsTo(mModeListView);
978         }
979     }
980
981     /**
982      * Gets called when activity resumes in preview.
983      */
984     public void resume() {
985         // Show mode theme cover until preview is ready
986         showModeCoverUntilPreviewReady();
987
988         // Hide action bar first since we are in full screen mode first, and
989         // switch the system UI to lights-out mode.
990         mFilmstripPanel.hide();
991
992         // Show UI that is meant to only be used when spoken feedback is
993         // enabled.
994         mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
995         mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
996     }
997
998     /**
999      * @return Whether any spoken feedback accessibility feature is currently
1000      *         enabled.
1001      */
1002     private boolean isSpokenFeedbackAccessibilityEnabled() {
1003         AccessibilityManager accessibilityManager = AndroidServices.instance()
1004               .provideAccessibilityManager();
1005         List<AccessibilityServiceInfo> infos = accessibilityManager
1006                 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1007         return infos != null && !infos.isEmpty();
1008     }
1009
1010     /**
1011      * Opens the mode list (e.g. because of the menu button being pressed) and
1012      * adapts the rest of the UI.
1013      */
1014     public void openModeList() {
1015         mModeOptionsOverlay.closeModeOptions();
1016         mModeListView.onMenuPressed();
1017     }
1018
1019     /**
1020      * A cover view showing the mode theme color and mode icon will be visible on
1021      * top of preview until preview is ready (i.e. camera preview is started and
1022      * the first frame has been received).
1023      */
1024     private void showModeCoverUntilPreviewReady() {
1025         int modeId = mController.getCurrentModuleIndex();
1026         int colorId = R.color.mode_cover_default_color;;
1027         int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1028         mModeTransitionView.setupModeCover(colorId, iconId);
1029         mHideCoverRunnable = new Runnable() {
1030             @Override
1031             public void run() {
1032                 mModeTransitionView.hideModeCover(null);
1033                 if (!mDisableAllUserInteractions) {
1034                     showShimmyDelayed();
1035                 }
1036             }
1037         };
1038         mModeCoverState = COVER_SHOWN;
1039     }
1040
1041     private void showShimmyDelayed() {
1042         if (!mIsCaptureIntent) {
1043             // Show shimmy in SHIMMY_DELAY_MS
1044             mModeListView.showModeSwitcherHint();
1045         }
1046     }
1047
1048     private void hideModeCover() {
1049         if (mHideCoverRunnable != null) {
1050             mAppRootView.post(mHideCoverRunnable);
1051             mHideCoverRunnable = null;
1052         }
1053         mModeCoverState = COVER_HIDDEN;
1054         if (mCoverHiddenTime < 0) {
1055             mCoverHiddenTime = System.currentTimeMillis();
1056         }
1057     }
1058
1059
1060     public void onPreviewVisiblityChanged(int visibility) {
1061         if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1062             setIndicatorBottomBarWrapperVisible(false);
1063             mAccessibilityAffordances.setVisibility(View.GONE);
1064         } else {
1065             setIndicatorBottomBarWrapperVisible(true);
1066             if (mAccessibilityEnabled) {
1067                 mAccessibilityAffordances.setVisibility(View.VISIBLE);
1068             } else {
1069                 mAccessibilityAffordances.setVisibility(View.GONE);
1070             }
1071         }
1072     }
1073
1074     /**
1075      * Call to stop the preview from being rendered.
1076      */
1077     public void pausePreviewRendering() {
1078         mTextureView.setVisibility(View.INVISIBLE);
1079     }
1080
1081     /**
1082      * Call to begin rendering the preview again.
1083      */
1084     public void resumePreviewRendering() {
1085         mTextureView.setVisibility(View.VISIBLE);
1086     }
1087
1088     /**
1089      * Returns the transform associated with the preview view.
1090      *
1091      * @param m the Matrix in which to copy the current transform.
1092      * @return The specified matrix if not null or a new Matrix instance
1093      *         otherwise.
1094      */
1095     public Matrix getPreviewTransform(Matrix m) {
1096         return mTextureView.getTransform(m);
1097     }
1098
1099     @Override
1100     public void onOpenFullScreen() {
1101         // Do nothing.
1102     }
1103
1104     @Override
1105     public void onModeListOpenProgress(float progress) {
1106         progress = 1 - progress;
1107         float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1108         mModeOptionsToggle.setAlpha(interpolatedProgress);
1109         // Change shutter button alpha linearly based on the mode list open progress:
1110         // set the alpha to disabled alpha when list is fully open, to enabled alpha
1111         // when the list is fully closed.
1112         mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1113                 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1114     }
1115
1116     @Override
1117     public void onModeListClosed() {
1118         // Make sure the alpha on mode options ellipse is reset when mode drawer
1119         // is closed.
1120         mModeOptionsToggle.setAlpha(1f);
1121         mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1122     }
1123
1124     /**
1125      * Called when the back key is pressed.
1126      *
1127      * @return Whether the UI responded to the key event.
1128      */
1129     public boolean onBackPressed() {
1130         if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1131             return mFilmstripLayout.onBackPressed();
1132         } else {
1133             return mModeListView.onBackPressed();
1134         }
1135     }
1136
1137     /**
1138      * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1139      * listens to SurfaceTexture changes. In addition, listeners are set on
1140      * dependent app ui elements.
1141      *
1142      * @param previewStatusListener the listener that gets notified when SurfaceTexture
1143      *                              changes
1144      */
1145     public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1146         mPreviewStatusListener = previewStatusListener;
1147         if (mPreviewStatusListener != null) {
1148             onPreviewListenerChanged();
1149         }
1150     }
1151
1152     /**
1153      * When the PreviewStatusListener changes, listeners need to be
1154      * set on the following app ui elements:
1155      * {@link com.android.camera.ui.PreviewOverlay},
1156      * {@link com.android.camera.ui.BottomBar},
1157      * {@link com.android.camera.ui.IndicatorIconController}.
1158      */
1159     private void onPreviewListenerChanged() {
1160         // Set a listener for recognizing preview gestures.
1161         GestureDetector.OnGestureListener gestureListener
1162             = mPreviewStatusListener.getGestureListener();
1163         if (gestureListener != null) {
1164             mPreviewOverlay.setGestureListener(gestureListener);
1165         }
1166         View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1167         if (touchListener != null) {
1168             mPreviewOverlay.setTouchListener(touchListener);
1169         }
1170
1171         mTextureViewHelper.setAutoAdjustTransform(
1172                 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1173     }
1174
1175     /**
1176      * This method should be called in onCameraOpened.  It defines CameraAppUI
1177      * specific changes that depend on the camera or camera settings.
1178      */
1179     public void onChangeCamera() {
1180         ModuleController moduleController = mController.getCurrentModuleController();
1181         applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1182
1183         if (mIndicatorIconController != null) {
1184             // Sync the settings state with the indicator state.
1185             mIndicatorIconController.syncIndicators();
1186         }
1187     }
1188
1189     /**
1190      * Adds a listener to receive callbacks when preview area changes.
1191      */
1192     public void addPreviewAreaChangedListener(
1193             PreviewStatusListener.PreviewAreaChangedListener listener) {
1194         mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1195     }
1196
1197     /**
1198      * Removes a listener that receives callbacks when preview area changes.
1199      */
1200     public void removePreviewAreaChangedListener(
1201             PreviewStatusListener.PreviewAreaChangedListener listener) {
1202         mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1203     }
1204
1205     /**
1206      * This inflates generic_module layout, which contains all the shared views across
1207      * modules. Then each module inflates their own views in the given view group. For
1208      * now, this is called every time switching from a not-yet-refactored module to a
1209      * refactored module. In the future, this should only need to be done once per app
1210      * start.
1211      */
1212     public void prepareModuleUI() {
1213         mController.getSettingsManager().addListener(this);
1214         mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1215         mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1216         mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1217                 mController.getCameraProvider());
1218         mTextureViewHelper.setSurfaceTextureListener(this);
1219         mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1220
1221         mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1222         int unpressedColor = mController.getAndroidContext().getResources()
1223             .getColor(R.color.bottombar_unpressed);
1224         setBottomBarColor(unpressedColor);
1225         updateModeSpecificUIColors();
1226
1227         mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1228
1229         mModeOptionsOverlay
1230             = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1231
1232         // Sets the visibility of the bottom bar and the mode options.
1233         resetBottomControls(mController.getCurrentModuleController(),
1234             mController.getCurrentModuleIndex());
1235         mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1236
1237         mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1238         addShutterListener(mController.getCurrentModuleController());
1239         addShutterListener(mModeOptionsOverlay);
1240         addShutterListener(this);
1241
1242         mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1243         mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1244
1245         mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1246         mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1247         mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1248
1249         mCaptureOverlay = (CaptureAnimationOverlay)
1250                 mCameraRootView.findViewById(R.id.capture_overlay);
1251         mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1252         mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1253
1254         if (mIndicatorIconController == null) {
1255             mIndicatorIconController =
1256                 new IndicatorIconController(mController, mAppRootView);
1257         }
1258
1259         mController.getButtonManager().load(mCameraRootView);
1260         mController.getButtonManager().setListener(mIndicatorIconController);
1261         mController.getSettingsManager().addListener(mIndicatorIconController);
1262
1263         mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1264         mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1265         mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1266                 .findViewById(R.id.tutorials_placeholder_wrapper);
1267         mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1268                 .findViewById(R.id.sticky_bottom_capture_layout);
1269         mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1270
1271         mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1272         mTextureViewHelper.addAspectRatioChangedListener(
1273                 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1274                     @Override
1275                     public void onPreviewAspectRatioChanged(float aspectRatio) {
1276                         mModeOptionsOverlay.requestLayout();
1277                         mBottomBar.requestLayout();
1278                     }
1279                 }
1280         );
1281     }
1282
1283     /**
1284      * Called indirectly from each module in their initialization to get a view group
1285      * to inflate the module specific views in.
1286      *
1287      * @return a view group for modules to attach views to
1288      */
1289     public FrameLayout getModuleRootView() {
1290         // TODO: Change it to mModuleUI when refactor is done
1291         return mCameraRootView;
1292     }
1293
1294     /**
1295      * Remove all the module specific views.
1296      */
1297     public void clearModuleUI() {
1298         if (mModuleUI != null) {
1299             mModuleUI.removeAllViews();
1300         }
1301         removeShutterListener(mController.getCurrentModuleController());
1302         mTutorialsPlaceHolderWrapper.removeAllViews();
1303         mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1304
1305         setShutterButtonEnabled(true);
1306         mPreviewStatusListener = null;
1307         mPreviewOverlay.reset();
1308
1309         Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1310         mFocusRing.stopFocusAnimations();
1311     }
1312
1313     /**
1314      * Gets called when preview is ready to start. It sets up one shot preview callback
1315      * in order to receive a callback when the preview frame is available, so that
1316      * the preview cover can be hidden to reveal preview.
1317      *
1318      * An alternative for getting the timing to hide preview cover is through
1319      * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1320      * which is less accurate but therefore is the fallback for modules that manage
1321      * their own preview callbacks (as setting one preview callback will override
1322      * any other installed preview callbacks), or use camera2 API.
1323      */
1324     public void onPreviewReadyToStart() {
1325         if (mModeCoverState == COVER_SHOWN) {
1326             mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1327             mController.setupOneShotPreviewListener();
1328         }
1329     }
1330
1331     /**
1332      * Gets called when preview is started.
1333      */
1334     public void onPreviewStarted() {
1335         Log.v(TAG, "onPreviewStarted");
1336         if (mModeCoverState == COVER_SHOWN) {
1337             mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1338         }
1339         enableModeOptions();
1340     }
1341
1342     /**
1343      * Gets notified when next preview frame comes in.
1344      */
1345     public void onNewPreviewFrame() {
1346         Log.v(TAG, "onNewPreviewFrame");
1347         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1348         hideModeCover();
1349     }
1350
1351     @Override
1352     public void onShutterButtonClick() {
1353         /*
1354          * Set the mode options toggle unclickable, generally
1355          * throughout the app, whenever the shutter button is clicked.
1356          *
1357          * This could be done in the OnShutterButtonListener of the
1358          * ModeOptionsOverlay, but since it is very important that we
1359          * can clearly see when the toggle becomes clickable again,
1360          * keep all of that logic at this level.
1361          */
1362         // disableModeOptions();
1363     }
1364
1365     @Override
1366     public void onShutterCoordinate(TouchCoordinate coord) {
1367         // Do nothing.
1368     }
1369
1370     @Override
1371     public void onShutterButtonFocus(boolean pressed) {
1372         // noop
1373     }
1374
1375     @Override
1376     public void onShutterButtonLongPressed() {
1377         // noop
1378     }
1379
1380     /**
1381      * Set the mode options toggle clickable.
1382      */
1383     public void enableModeOptions() {
1384         /*
1385          * For modules using camera 1 api, this gets called in
1386          * onSurfaceTextureUpdated whenever the preview gets stopped and
1387          * started after each capture.  This also takes care of the
1388          * case where the mode options might be unclickable when we
1389          * switch modes
1390          *
1391          * For modules using camera 2 api, they're required to call this
1392          * method when a capture is "completed".  Unfortunately this differs
1393          * per module implementation.
1394          */
1395         if (!mDisableAllUserInteractions) {
1396             mModeOptionsOverlay.setToggleClickable(true);
1397         }
1398     }
1399
1400     /**
1401      * Set the mode options toggle not clickable.
1402      */
1403     public void disableModeOptions() {
1404         mModeOptionsOverlay.setToggleClickable(false);
1405     }
1406
1407     public void setDisableAllUserInteractions(boolean disable) {
1408         if (disable) {
1409             disableModeOptions();
1410             setShutterButtonEnabled(false);
1411             setSwipeEnabled(false);
1412             mModeListView.hideAnimated();
1413         } else {
1414             enableModeOptions();
1415             setShutterButtonEnabled(true);
1416             setSwipeEnabled(true);
1417         }
1418         mDisableAllUserInteractions = disable;
1419     }
1420
1421     @Override
1422     public void onModeButtonPressed(int modeIndex) {
1423         // TODO: Make CameraActivity listen to ModeListView's events.
1424         int pressedModuleId = mController.getModuleId(modeIndex);
1425         int currentModuleId = mController.getCurrentModuleIndex();
1426         if (pressedModuleId != currentModuleId) {
1427             hideCaptureIndicator();
1428         }
1429     }
1430
1431     /**
1432      * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1433      *
1434      * @param modeIndex mode index of the selected mode
1435      */
1436     @Override
1437     public void onModeSelected(int modeIndex) {
1438         mHideCoverRunnable = new Runnable() {
1439             @Override
1440             public void run() {
1441                 mModeListView.startModeSelectionAnimation();
1442             }
1443         };
1444         mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1445         mModeCoverState = COVER_SHOWN;
1446
1447         int lastIndex = mController.getCurrentModuleIndex();
1448         // Actual mode teardown / new mode initialization happens here
1449         mController.onModeSelected(modeIndex);
1450         int currentIndex = mController.getCurrentModuleIndex();
1451
1452         if (lastIndex == currentIndex) {
1453             hideModeCover();
1454         }
1455
1456         updateModeSpecificUIColors();
1457     }
1458
1459     private void updateModeSpecificUIColors() {
1460         setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1461     }
1462
1463     @Override
1464     public void onSettingsSelected() {
1465         mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1466                                              Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1467         mModeListView.setShouldShowSettingsCling(false);
1468         mController.onSettingsSelected();
1469     }
1470
1471     @Override
1472     public int getCurrentModeIndex() {
1473         return mController.getCurrentModuleIndex();
1474     }
1475
1476     /********************** Capture animation **********************/
1477     /* TODO: This session is subject to UX changes. In addition to the generic
1478        flash animation and post capture animation, consider designating a parameter
1479        for specifying the type of animation, as well as an animation finished listener
1480        so that modules can have more knowledge of the status of the animation. */
1481
1482     /**
1483      * Starts the capture indicator pop-out animation.
1484      *
1485      * @param accessibilityString An accessibility String to be announced during the peek animation.
1486      */
1487     public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1488         if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1489             return;
1490         }
1491         mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1492     }
1493
1494     /**
1495      * Updates the thumbnail image in the capture indicator.
1496      *
1497      * @param thumbnailBitmap The thumbnail image to be shown.
1498      */
1499     public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
1500         mRoundedThumbnailView.setThumbnail(thumbnailBitmap);
1501         mRoundedThumbnailView.setRotation(rotation);
1502     }
1503
1504     /**
1505      * Hides the capture indicator.
1506      */
1507     public void hideCaptureIndicator() {
1508         mRoundedThumbnailView.hideThumbnail();
1509     }
1510
1511     /**
1512      * Starts the flash animation.
1513      */
1514     public void startFlashAnimation(boolean shortFlash) {
1515         mCaptureOverlay.startFlashAnimation(shortFlash);
1516     }
1517
1518     /**
1519      * Cancels the pre-capture animation.
1520      */
1521     public void cancelPreCaptureAnimation() {
1522         mAnimationManager.cancelAnimations();
1523     }
1524
1525     /**
1526      * Cancels the post-capture animation.
1527      */
1528     public void cancelPostCaptureAnimation() {
1529         mAnimationManager.cancelAnimations();
1530     }
1531
1532     public FilmstripContentPanel getFilmstripContentPanel() {
1533         return mFilmstripPanel;
1534     }
1535
1536     /**
1537      * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1538      * bottom of the filmstrip.
1539      */
1540     public BottomPanel getFilmstripBottomControls() {
1541         return mFilmstripBottomControls;
1542     }
1543
1544     public void showBottomControls() {
1545         mFilmstripBottomControls.show();
1546     }
1547
1548     public void hideBottomControls() {
1549         mFilmstripBottomControls.hide();
1550     }
1551
1552     /**
1553      * @param listener The listener for bottom controls.
1554      */
1555     public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1556         mFilmstripBottomControls.setListener(listener);
1557     }
1558
1559     /***************************SurfaceTexture Api and Listener*********************************/
1560
1561     /**
1562      * Return the shared surface texture.
1563      */
1564     public SurfaceTexture getSurfaceTexture() {
1565         return mSurface;
1566     }
1567
1568     /**
1569      * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1570      */
1571     public int getSurfaceWidth() {
1572         return mSurfaceWidth;
1573     }
1574
1575     /**
1576      * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1577      */
1578     public int getSurfaceHeight() {
1579         return mSurfaceHeight;
1580     }
1581
1582     @Override
1583     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1584         mSurface = surface;
1585         mSurfaceWidth = width;
1586         mSurfaceHeight = height;
1587         Log.v(TAG, "SurfaceTexture is available");
1588         if (mPreviewStatusListener != null) {
1589             mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1590         }
1591         enableModeOptions();
1592     }
1593
1594     @Override
1595     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1596         mSurface = surface;
1597         mSurfaceWidth = width;
1598         mSurfaceHeight = height;
1599         if (mPreviewStatusListener != null) {
1600             mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1601         }
1602     }
1603
1604     @Override
1605     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1606         mSurface = null;
1607         Log.v(TAG, "SurfaceTexture is destroyed");
1608         if (mPreviewStatusListener != null) {
1609             return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1610         }
1611         return false;
1612     }
1613
1614     @Override
1615     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1616         mSurface = surface;
1617         if (mPreviewStatusListener != null) {
1618             mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1619         }
1620         if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1621             Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1622             CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1623             hideModeCover();
1624         }
1625     }
1626
1627     /****************************Grid lines api ******************************/
1628
1629     /**
1630      * Show a set of evenly spaced lines over the preview.  The number
1631      * of lines horizontally and vertically is determined by
1632      * {@link com.android.camera.ui.GridLines}.
1633      */
1634     public void showGridLines() {
1635         if (mGridLines != null) {
1636             mGridLines.setVisibility(View.VISIBLE);
1637         }
1638     }
1639
1640     /**
1641      * Hide the set of evenly spaced grid lines overlaying the preview.
1642      */
1643     public void hideGridLines() {
1644         if (mGridLines != null) {
1645             mGridLines.setVisibility(View.INVISIBLE);
1646         }
1647     }
1648
1649     /**
1650      * Return a callback which shows or hide the preview grid lines
1651      * depending on whether the grid lines setting is set on.
1652      */
1653     public ButtonManager.ButtonCallback getGridLinesCallback() {
1654         return new ButtonManager.ButtonCallback() {
1655             @Override
1656             public void onStateChanged(int state) {
1657                 if (!mController.isPaused()) {
1658                     if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1659                         showGridLines();
1660                     } else {
1661                         hideGridLines();
1662                     }
1663                 }
1664             }
1665         };
1666     }
1667
1668     /***************************Mode options api *****************************/
1669
1670     /**
1671      * Set the mode options visible.
1672      */
1673     public void showModeOptions() {
1674         /* Make mode options clickable. */
1675         enableModeOptions();
1676         mModeOptionsOverlay.setVisibility(View.VISIBLE);
1677     }
1678
1679     /**
1680      * Set the mode options invisible.  This is necessary for modes
1681      * that don't show a bottom bar for the capture UI.
1682      */
1683     public void hideModeOptions() {
1684         mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1685     }
1686
1687     /****************************Bottom bar api ******************************/
1688
1689     /**
1690      * Sets up the bottom bar and mode options with the correct
1691      * shutter button and visibility based on the current module.
1692      */
1693     public void resetBottomControls(ModuleController module, int moduleIndex) {
1694         if (areBottomControlsUsed(module)) {
1695             setBottomBarShutterIcon(moduleIndex);
1696             mCaptureLayoutHelper.setShowBottomBar(true);
1697         } else {
1698             mCaptureLayoutHelper.setShowBottomBar(false);
1699         }
1700     }
1701
1702     /**
1703      * Show or hide the mode options and bottom bar, based on
1704      * whether the current module is using the bottom bar.  Returns
1705      * whether the mode options and bottom bar are used.
1706      */
1707     private boolean areBottomControlsUsed(ModuleController module) {
1708         if (module.isUsingBottomBar()) {
1709             showBottomBar();
1710             showModeOptions();
1711             return true;
1712         } else {
1713             hideBottomBar();
1714             hideModeOptions();
1715             return false;
1716         }
1717     }
1718
1719     /**
1720      * Set the bottom bar visible.
1721      */
1722     public void showBottomBar() {
1723         mBottomBar.setVisibility(View.VISIBLE);
1724     }
1725
1726     /**
1727      * Set the bottom bar invisible.
1728      */
1729     public void hideBottomBar() {
1730         mBottomBar.setVisibility(View.INVISIBLE);
1731     }
1732
1733     /**
1734      * Sets the color of the bottom bar.
1735      */
1736     public void setBottomBarColor(int colorId) {
1737         mBottomBar.setBackgroundColor(colorId);
1738     }
1739
1740     /**
1741      * Sets the pressed color of the bottom bar for a camera mode index.
1742      */
1743     public void setBottomBarColorsForModeIndex(int index) {
1744         mBottomBar.setColorsForModeIndex(index);
1745     }
1746
1747     /**
1748      * Sets the shutter button icon on the bottom bar, based on
1749      * the mode index.
1750      */
1751     public void setBottomBarShutterIcon(int modeIndex) {
1752         int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1753             mController.getAndroidContext());
1754         mBottomBar.setShutterButtonIcon(shutterIconId);
1755     }
1756
1757     public void animateBottomBarToVideoStop(int shutterIconId) {
1758         mBottomBar.animateToVideoStop(shutterIconId);
1759     }
1760
1761     public void animateBottomBarToFullSize(int shutterIconId) {
1762         mBottomBar.animateToFullSize(shutterIconId);
1763     }
1764
1765     public void setShutterButtonEnabled(final boolean enabled) {
1766         if (!mDisableAllUserInteractions) {
1767             mBottomBar.post(new Runnable() {
1768                 @Override
1769                 public void run() {
1770                     mBottomBar.setShutterButtonEnabled(enabled);
1771                 }
1772             });
1773         }
1774     }
1775
1776     public void setShutterButtonImportantToA11y(boolean important) {
1777         mBottomBar.setShutterButtonImportantToA11y(important);
1778     }
1779
1780     public boolean isShutterButtonEnabled() {
1781         return mBottomBar.isShutterButtonEnabled();
1782     }
1783
1784     public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1785         mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1786     }
1787
1788     /**
1789      * Set the visibility of the bottom bar.
1790      */
1791     // TODO: needed for when panorama is managed by the generic module ui.
1792     public void setBottomBarVisible(boolean visible) {
1793         mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1794     }
1795
1796     /**
1797      * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1798      */
1799     public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1800         mShutterButton.addOnShutterButtonListener(listener);
1801     }
1802
1803     /**
1804      * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1805      */
1806     public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1807         mShutterButton.removeOnShutterButtonListener(listener);
1808     }
1809
1810     /**
1811      * Performs a transition to the capture layout of the bottom bar.
1812      */
1813     public void transitionToCapture() {
1814         ModuleController moduleController = mController.getCurrentModuleController();
1815         applyModuleSpecs(moduleController.getHardwareSpec(),
1816             moduleController.getBottomBarSpec());
1817         mBottomBar.transitionToCapture();
1818     }
1819
1820     /**
1821      * Displays the Cancel button instead of the capture button.
1822      */
1823     public void transitionToCancel() {
1824         ModuleController moduleController = mController.getCurrentModuleController();
1825         applyModuleSpecs(moduleController.getHardwareSpec(),
1826                 moduleController.getBottomBarSpec());
1827         mBottomBar.transitionToCancel();
1828     }
1829
1830     /**
1831      * Performs a transition to the global intent layout.
1832      */
1833     public void transitionToIntentCaptureLayout() {
1834         ModuleController moduleController = mController.getCurrentModuleController();
1835         applyModuleSpecs(moduleController.getHardwareSpec(),
1836             moduleController.getBottomBarSpec());
1837         mBottomBar.transitionToIntentCaptureLayout();
1838     }
1839
1840     /**
1841      * Performs a transition to the global intent review layout.
1842      */
1843     public void transitionToIntentReviewLayout() {
1844         ModuleController moduleController = mController.getCurrentModuleController();
1845         applyModuleSpecs(moduleController.getHardwareSpec(),
1846             moduleController.getBottomBarSpec());
1847         mBottomBar.transitionToIntentReviewLayout();
1848     }
1849
1850     /**
1851      * @return whether UI is in intent review mode
1852      */
1853     public boolean isInIntentReview() {
1854         return mBottomBar.isInIntentReview();
1855     }
1856
1857     @Override
1858     public void onSettingChanged(SettingsManager settingsManager, String key) {
1859         // Update the mode options based on the hardware spec,
1860         // when hdr changes to prevent flash from getting out of sync.
1861         if (key.equals(Keys.KEY_CAMERA_HDR)) {
1862             ModuleController moduleController = mController.getCurrentModuleController();
1863             applyModuleSpecs(moduleController.getHardwareSpec(),
1864                              moduleController.getBottomBarSpec());
1865         }
1866     }
1867
1868     /**
1869      * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1870      * to the bottom bar mode options based on limitations from a
1871      * {@link com.android.camera.hardware.HardwareSpec}.
1872      *
1873      * Options not supported by the hardware are either hidden
1874      * or disabled, depending on the option.
1875      *
1876      * Otherwise, the option is fully enabled and clickable.
1877      */
1878     public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1879            final BottomBarUISpec bottomBarSpec) {
1880         if (hardwareSpec == null || bottomBarSpec == null) {
1881             return;
1882         }
1883
1884         ButtonManager buttonManager = mController.getButtonManager();
1885         SettingsManager settingsManager = mController.getSettingsManager();
1886
1887         buttonManager.setToInitialState();
1888
1889         /** Standard mode options */
1890         if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1891                 hardwareSpec.isFrontCameraSupported()) {
1892             if (bottomBarSpec.enableCamera) {
1893                 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1894                         bottomBarSpec.cameraCallback);
1895             } else {
1896                 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1897             }
1898         } else {
1899             // Hide camera icon if front camera not available.
1900             buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1901         }
1902
1903         if (bottomBarSpec.hideFlash) {
1904             // Hide both flash and torch button in flash disable logic
1905             buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1906             buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1907         } else {
1908             if (hardwareSpec.isFlashSupported()) {
1909                 if (bottomBarSpec.enableFlash) {
1910                     buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1911                         bottomBarSpec.flashCallback);
1912                 } else if (bottomBarSpec.enableTorchFlash) {
1913                     buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1914                         bottomBarSpec.flashCallback);
1915                 } else if (bottomBarSpec.enableHdrPlusFlash) {
1916                     buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1917                         bottomBarSpec.flashCallback);
1918                 } else {
1919                     // Hide both flash and torch button in flash disable logic
1920                     buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1921                     buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1922                 }
1923             } else {
1924                 // Disable both flash and torch icon if not supported
1925                 // by the chosen camera hardware.
1926                 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1927                 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1928             }
1929         }
1930
1931         if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1932             // Force hide hdr or hdr plus icon.
1933             buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1934         } else {
1935             if (hardwareSpec.isHdrPlusSupported()) {
1936                 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1937                                                                        mController.getModuleScope())) {
1938                     buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1939                             bottomBarSpec.hdrCallback);
1940                 } else {
1941                     buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1942                 }
1943             } else if (hardwareSpec.isHdrSupported()) {
1944                 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1945                                                                        mController.getModuleScope())) {
1946                     buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1947                             bottomBarSpec.hdrCallback);
1948                 } else {
1949                     buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1950                 }
1951             } else {
1952                 // Hide hdr plus or hdr icon if neither are supported.
1953                 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1954             }
1955         }
1956
1957         if (bottomBarSpec.hideGridLines) {
1958             // Force hide grid lines icon.
1959             buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1960             hideGridLines();
1961         } else {
1962             if (bottomBarSpec.enableGridLines) {
1963                 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1964                         bottomBarSpec.gridLinesCallback != null ?
1965                                 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1966                 );
1967             } else {
1968                 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1969                 hideGridLines();
1970             }
1971         }
1972
1973         if (bottomBarSpec.enableSelfTimer) {
1974             buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
1975         } else {
1976             if (bottomBarSpec.showSelfTimer) {
1977                 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
1978             } else {
1979                 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
1980             }
1981         }
1982
1983         if (bottomBarSpec.enablePanoOrientation
1984                 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
1985             buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
1986         }
1987
1988         boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
1989             !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
1990             mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
1991                         Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
1992         if (enableExposureCompensation) {
1993             buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
1994             buttonManager.setExposureCompensationParameters(
1995                 bottomBarSpec.minExposureCompensation,
1996                 bottomBarSpec.maxExposureCompensation,
1997                 bottomBarSpec.exposureCompensationStep);
1998
1999             buttonManager.setExposureCompensationCallback(
2000                     bottomBarSpec.exposureCompensationSetCallback);
2001             buttonManager.updateExposureButtons();
2002         } else {
2003             buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2004             buttonManager.setExposureCompensationCallback(null);
2005         }
2006
2007         /** Intent UI */
2008         if (bottomBarSpec.showCancel) {
2009             buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2010                     bottomBarSpec.cancelCallback);
2011         }
2012         if (bottomBarSpec.showDone) {
2013             buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2014                     bottomBarSpec.doneCallback);
2015         }
2016         if (bottomBarSpec.showRetake) {
2017             buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2018                     bottomBarSpec.retakeCallback);
2019         }
2020         if (bottomBarSpec.showReview) {
2021             buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2022                     bottomBarSpec.reviewCallback,
2023                     R.drawable.ic_play);
2024         }
2025     }
2026
2027     /**
2028      * Shows the given tutorial on the screen.
2029      */
2030     public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2031         tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2032     }
2033
2034     /***************************Filmstrip api *****************************/
2035
2036     public void showFilmstrip() {
2037         mModeListView.onBackPressed();
2038         mFilmstripLayout.showFilmstrip();
2039     }
2040
2041     public void hideFilmstrip() {
2042         mFilmstripLayout.hideFilmstrip();
2043     }
2044 }