OSDN Git Service

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