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