OSDN Git Service

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