CameraAppUI.java revision 86f1d1f3664c909d1d949951056c7bb1e9627324
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
17package com.android.camera.app;
18
19import android.content.res.Resources;
20import android.graphics.Bitmap;
21import android.graphics.Canvas;
22import android.graphics.Matrix;
23import android.graphics.RectF;
24import android.graphics.SurfaceTexture;
25import android.hardware.display.DisplayManager;
26import android.util.CameraPerformanceTracker;
27import android.view.GestureDetector;
28import android.view.LayoutInflater;
29import android.view.MotionEvent;
30import android.view.TextureView;
31import android.view.View;
32import android.view.ViewConfiguration;
33import android.view.ViewGroup;
34import android.widget.FrameLayout;
35import android.widget.ImageButton;
36
37import com.android.camera.AccessibilityUtil;
38import com.android.camera.AnimationManager;
39import com.android.camera.ButtonManager;
40import com.android.camera.CaptureLayoutHelper;
41import com.android.camera.ShutterButton;
42import com.android.camera.TextureViewHelper;
43import com.android.camera.debug.Log;
44import com.android.camera.filmstrip.FilmstripContentPanel;
45import com.android.camera.hardware.HardwareSpec;
46import com.android.camera.module.ModuleController;
47import com.android.camera.settings.Keys;
48import com.android.camera.settings.SettingsManager;
49import com.android.camera.ui.AbstractTutorialOverlay;
50import com.android.camera.ui.BottomBar;
51import com.android.camera.ui.CaptureAnimationOverlay;
52import com.android.camera.ui.GridLines;
53import com.android.camera.ui.MainActivityLayout;
54import com.android.camera.ui.ModeListView;
55import com.android.camera.ui.ModeTransitionView;
56import com.android.camera.ui.PreviewOverlay;
57import com.android.camera.ui.PreviewStatusListener;
58import com.android.camera.ui.StickyBottomCaptureLayout;
59import com.android.camera.ui.TouchCoordinate;
60import com.android.camera.ui.focus.FocusRing;
61import com.android.camera.util.AndroidServices;
62import com.android.camera.util.ApiHelper;
63import com.android.camera.util.CameraUtil;
64import com.android.camera.util.Gusterpolator;
65import com.android.camera.util.PhotoSphereHelper;
66import com.android.camera.widget.Cling;
67import com.android.camera.widget.FilmstripLayout;
68import com.android.camera.widget.IndicatorIconController;
69import com.android.camera.widget.ModeOptionsOverlay;
70import com.android.camera.widget.RoundedThumbnailView;
71import 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 */
85public 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
2115    public int getFilmstripVisibility() {
2116        return mFilmstripLayout.getVisibility();
2117    }
2118}
2119