CameraAppUI.java revision 46aac2cd824dceb30afebf1c8da955e4fa55ac5c
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        syncModeOptionIndicators();
1218    }
1219
1220    /**
1221     * Updates the mode option indicators according to the current settings.
1222     */
1223    public void syncModeOptionIndicators() {
1224        if (mIndicatorIconController != null) {
1225            // Sync the settings state with the indicator state.
1226            mIndicatorIconController.syncIndicators();
1227        }
1228    }
1229
1230    /**
1231     * Adds a listener to receive callbacks when preview area changes.
1232     */
1233    public void addPreviewAreaChangedListener(
1234            PreviewStatusListener.PreviewAreaChangedListener listener) {
1235        mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1236    }
1237
1238    /**
1239     * Removes a listener that receives callbacks when preview area changes.
1240     */
1241    public void removePreviewAreaChangedListener(
1242            PreviewStatusListener.PreviewAreaChangedListener listener) {
1243        mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1244    }
1245
1246    /**
1247     * This inflates generic_module layout, which contains all the shared views across
1248     * modules. Then each module inflates their own views in the given view group. For
1249     * now, this is called every time switching from a not-yet-refactored module to a
1250     * refactored module. In the future, this should only need to be done once per app
1251     * start.
1252     */
1253    public void prepareModuleUI() {
1254        mController.getSettingsManager().addListener(this);
1255        mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1256        mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1257        mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1258                mController.getCameraProvider());
1259        mTextureViewHelper.setSurfaceTextureListener(this);
1260        mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1261
1262        mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1263        int unpressedColor = mController.getAndroidContext().getResources()
1264            .getColor(R.color.camera_gray_background);
1265        setBottomBarColor(unpressedColor);
1266        updateModeSpecificUIColors();
1267
1268        mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1269
1270        mModeOptionsOverlay
1271            = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1272
1273        // Sets the visibility of the bottom bar and the mode options.
1274        resetBottomControls(mController.getCurrentModuleController(),
1275            mController.getCurrentModuleIndex());
1276        mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1277
1278        mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1279        addShutterListener(mController.getCurrentModuleController());
1280        addShutterListener(mModeOptionsOverlay);
1281        addShutterListener(this);
1282
1283        mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1284        mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1285
1286        mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1287        mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1288        mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1289        mAccessibilityUtil = new AccessibilityUtil(mPreviewOverlay, mAccessibilityAffordances);
1290
1291        mCaptureOverlay = (CaptureAnimationOverlay)
1292                mCameraRootView.findViewById(R.id.capture_overlay);
1293        mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1294        mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1295
1296        if (mIndicatorIconController == null) {
1297            mIndicatorIconController =
1298                new IndicatorIconController(mController, mAppRootView);
1299        }
1300
1301        mController.getButtonManager().load(mCameraRootView);
1302        mController.getButtonManager().setListener(mIndicatorIconController);
1303        mController.getSettingsManager().addListener(mIndicatorIconController);
1304
1305        mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1306        mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1307        mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1308                .findViewById(R.id.tutorials_placeholder_wrapper);
1309        mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1310                .findViewById(R.id.sticky_bottom_capture_layout);
1311        mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1312        mCountdownCancelButton = (ImageButton) mStickyBottomCaptureLayout
1313                .findViewById(R.id.shutter_cancel_button);
1314
1315        mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1316        mTextureViewHelper.addAspectRatioChangedListener(
1317                new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1318                    @Override
1319                    public void onPreviewAspectRatioChanged(float aspectRatio) {
1320                        mModeOptionsOverlay.requestLayout();
1321                        mBottomBar.requestLayout();
1322                    }
1323                }
1324        );
1325    }
1326
1327    /**
1328     * Called indirectly from each module in their initialization to get a view group
1329     * to inflate the module specific views in.
1330     *
1331     * @return a view group for modules to attach views to
1332     */
1333    public FrameLayout getModuleRootView() {
1334        // TODO: Change it to mModuleUI when refactor is done
1335        return mCameraRootView;
1336    }
1337
1338    /**
1339     * Remove all the module specific views.
1340     */
1341    public void clearModuleUI() {
1342        if (mModuleUI != null) {
1343            mModuleUI.removeAllViews();
1344        }
1345        removeShutterListener(mController.getCurrentModuleController());
1346        mTutorialsPlaceHolderWrapper.removeAllViews();
1347        mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1348
1349        setShutterButtonEnabled(true);
1350        mPreviewStatusListener = null;
1351        mPreviewOverlay.reset();
1352
1353        Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1354        mFocusRing.stopFocusAnimations();
1355    }
1356
1357    /**
1358     * Gets called when preview is ready to start. It sets up one shot preview callback
1359     * in order to receive a callback when the preview frame is available, so that
1360     * the preview cover can be hidden to reveal preview.
1361     *
1362     * An alternative for getting the timing to hide preview cover is through
1363     * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1364     * which is less accurate but therefore is the fallback for modules that manage
1365     * their own preview callbacks (as setting one preview callback will override
1366     * any other installed preview callbacks), or use camera2 API.
1367     */
1368    public void onPreviewReadyToStart() {
1369        if (mModeCoverState == COVER_SHOWN) {
1370            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1371            mController.setupOneShotPreviewListener();
1372        }
1373    }
1374
1375    /**
1376     * Gets called when preview is started.
1377     */
1378    public void onPreviewStarted() {
1379        Log.v(TAG, "onPreviewStarted");
1380        if (mModeCoverState == COVER_SHOWN) {
1381            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1382        }
1383        enableModeOptions();
1384    }
1385
1386    /**
1387     * Gets notified when next preview frame comes in.
1388     */
1389    public void onNewPreviewFrame() {
1390        Log.v(TAG, "onNewPreviewFrame");
1391        CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1392        hideModeCover();
1393    }
1394
1395    @Override
1396    public void onShutterButtonClick() {
1397        /*
1398         * Set the mode options toggle unclickable, generally
1399         * throughout the app, whenever the shutter button is clicked.
1400         *
1401         * This could be done in the OnShutterButtonListener of the
1402         * ModeOptionsOverlay, but since it is very important that we
1403         * can clearly see when the toggle becomes clickable again,
1404         * keep all of that logic at this level.
1405         */
1406        // disableModeOptions();
1407    }
1408
1409    @Override
1410    public void onShutterCoordinate(TouchCoordinate coord) {
1411        // Do nothing.
1412    }
1413
1414    @Override
1415    public void onShutterButtonFocus(boolean pressed) {
1416        // noop
1417    }
1418
1419    @Override
1420    public void onShutterButtonLongPressed() {
1421        // noop
1422    }
1423
1424    /**
1425     * Set the mode options toggle clickable.
1426     */
1427    public void enableModeOptions() {
1428        /*
1429         * For modules using camera 1 api, this gets called in
1430         * onSurfaceTextureUpdated whenever the preview gets stopped and
1431         * started after each capture.  This also takes care of the
1432         * case where the mode options might be unclickable when we
1433         * switch modes
1434         *
1435         * For modules using camera 2 api, they're required to call this
1436         * method when a capture is "completed".  Unfortunately this differs
1437         * per module implementation.
1438         */
1439        if (!mDisableAllUserInteractions) {
1440            mModeOptionsOverlay.setToggleClickable(true);
1441        }
1442    }
1443
1444    /**
1445     * Set the mode options toggle not clickable.
1446     */
1447    public void disableModeOptions() {
1448        mModeOptionsOverlay.setToggleClickable(false);
1449    }
1450
1451    public void setDisableAllUserInteractions(boolean disable) {
1452        if (disable) {
1453            disableModeOptions();
1454            setShutterButtonEnabled(false);
1455            setSwipeEnabled(false);
1456            mModeListView.hideAnimated();
1457        } else {
1458            enableModeOptions();
1459            setShutterButtonEnabled(true);
1460            setSwipeEnabled(true);
1461        }
1462        mDisableAllUserInteractions = disable;
1463    }
1464
1465    @Override
1466    public void onModeButtonPressed(int modeIndex) {
1467        // TODO: Make CameraActivity listen to ModeListView's events.
1468        int pressedModuleId = mController.getModuleId(modeIndex);
1469        int currentModuleId = mController.getCurrentModuleIndex();
1470        if (pressedModuleId != currentModuleId) {
1471            hideCaptureIndicator();
1472        }
1473    }
1474
1475    /**
1476     * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1477     *
1478     * @param modeIndex mode index of the selected mode
1479     */
1480    @Override
1481    public void onModeSelected(int modeIndex) {
1482        mHideCoverRunnable = new Runnable() {
1483            @Override
1484            public void run() {
1485                mModeListView.startModeSelectionAnimation();
1486            }
1487        };
1488        mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1489        mModeCoverState = COVER_SHOWN;
1490
1491        int lastIndex = mController.getCurrentModuleIndex();
1492        // Actual mode teardown / new mode initialization happens here
1493        mController.onModeSelected(modeIndex);
1494        int currentIndex = mController.getCurrentModuleIndex();
1495
1496        if (lastIndex == currentIndex) {
1497            hideModeCover();
1498        }
1499
1500        updateModeSpecificUIColors();
1501    }
1502
1503    private void updateModeSpecificUIColors() {
1504        setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1505    }
1506
1507    @Override
1508    public void onSettingsSelected() {
1509        mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1510                                             Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1511        mModeListView.setShouldShowSettingsCling(false);
1512        mController.onSettingsSelected();
1513    }
1514
1515    @Override
1516    public int getCurrentModeIndex() {
1517        return mController.getCurrentModuleIndex();
1518    }
1519
1520    /********************** Capture animation **********************/
1521    /* TODO: This session is subject to UX changes. In addition to the generic
1522       flash animation and post capture animation, consider designating a parameter
1523       for specifying the type of animation, as well as an animation finished listener
1524       so that modules can have more knowledge of the status of the animation. */
1525
1526    /**
1527     * Turns on or off the capture indicator suppression.
1528     */
1529    public void setShouldSuppressCaptureIndicator(boolean suppress) {
1530        mSuppressCaptureIndicator = suppress;
1531    }
1532
1533    /**
1534     * Starts the capture indicator pop-out animation.
1535     *
1536     * @param accessibilityString An accessibility String to be announced during the peek animation.
1537     */
1538    public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1539        if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
1540            return;
1541        }
1542        mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1543    }
1544
1545    /**
1546     * Updates the thumbnail image in the capture indicator.
1547     *
1548     * @param thumbnailBitmap The thumbnail image to be shown.
1549     */
1550    public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
1551        if (mSuppressCaptureIndicator || mFilmstripLayout.getVisibility() == View.VISIBLE) {
1552            return;
1553        }
1554        mRoundedThumbnailView.setThumbnail(thumbnailBitmap, rotation);
1555    }
1556
1557    /**
1558     * Hides the capture indicator.
1559     */
1560    public void hideCaptureIndicator() {
1561        mRoundedThumbnailView.hideThumbnail();
1562    }
1563
1564    /**
1565     * Starts the flash animation.
1566     */
1567    public void startFlashAnimation(boolean shortFlash) {
1568        mCaptureOverlay.startFlashAnimation(shortFlash);
1569    }
1570
1571    /**
1572     * Cancels the pre-capture animation.
1573     */
1574    public void cancelPreCaptureAnimation() {
1575        mAnimationManager.cancelAnimations();
1576    }
1577
1578    /**
1579     * Cancels the post-capture animation.
1580     */
1581    public void cancelPostCaptureAnimation() {
1582        mAnimationManager.cancelAnimations();
1583    }
1584
1585    public FilmstripContentPanel getFilmstripContentPanel() {
1586        return mFilmstripPanel;
1587    }
1588
1589    /**
1590     * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1591     * bottom of the filmstrip.
1592     */
1593    public BottomPanel getFilmstripBottomControls() {
1594        return mFilmstripBottomControls;
1595    }
1596
1597    public void showBottomControls() {
1598        mFilmstripBottomControls.show();
1599    }
1600
1601    public void hideBottomControls() {
1602        mFilmstripBottomControls.hide();
1603    }
1604
1605    /**
1606     * @param listener The listener for bottom controls.
1607     */
1608    public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1609        mFilmstripBottomControls.setListener(listener);
1610    }
1611
1612    /***************************SurfaceTexture Api and Listener*********************************/
1613
1614    /**
1615     * Return the shared surface texture.
1616     */
1617    public SurfaceTexture getSurfaceTexture() {
1618        return mSurface;
1619    }
1620
1621    /**
1622     * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1623     */
1624    public int getSurfaceWidth() {
1625        return mSurfaceWidth;
1626    }
1627
1628    /**
1629     * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1630     */
1631    public int getSurfaceHeight() {
1632        return mSurfaceHeight;
1633    }
1634
1635    @Override
1636    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1637        mSurface = surface;
1638        mSurfaceWidth = width;
1639        mSurfaceHeight = height;
1640        Log.v(TAG, "SurfaceTexture is available");
1641        if (mPreviewStatusListener != null) {
1642            mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1643        }
1644        enableModeOptions();
1645    }
1646
1647    @Override
1648    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1649        mSurface = surface;
1650        mSurfaceWidth = width;
1651        mSurfaceHeight = height;
1652        if (mPreviewStatusListener != null) {
1653            mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1654        }
1655    }
1656
1657    @Override
1658    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1659        mSurface = null;
1660        Log.v(TAG, "SurfaceTexture is destroyed");
1661        if (mPreviewStatusListener != null) {
1662            return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1663        }
1664        return false;
1665    }
1666
1667    @Override
1668    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1669        mSurface = surface;
1670        if (mPreviewStatusListener != null) {
1671            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1672        }
1673        if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1674            Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1675            CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1676            hideModeCover();
1677        }
1678    }
1679
1680    /****************************Grid lines api ******************************/
1681
1682    /**
1683     * Show a set of evenly spaced lines over the preview.  The number
1684     * of lines horizontally and vertically is determined by
1685     * {@link com.android.camera.ui.GridLines}.
1686     */
1687    public void showGridLines() {
1688        if (mGridLines != null) {
1689            mGridLines.setVisibility(View.VISIBLE);
1690        }
1691    }
1692
1693    /**
1694     * Hide the set of evenly spaced grid lines overlaying the preview.
1695     */
1696    public void hideGridLines() {
1697        if (mGridLines != null) {
1698            mGridLines.setVisibility(View.INVISIBLE);
1699        }
1700    }
1701
1702    /**
1703     * Return a callback which shows or hide the preview grid lines
1704     * depending on whether the grid lines setting is set on.
1705     */
1706    public ButtonManager.ButtonCallback getGridLinesCallback() {
1707        return new ButtonManager.ButtonCallback() {
1708            @Override
1709            public void onStateChanged(int state) {
1710                if (!mController.isPaused()) {
1711                    if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1712                        showGridLines();
1713                    } else {
1714                        hideGridLines();
1715                    }
1716                }
1717            }
1718        };
1719    }
1720
1721    /***************************Mode options api *****************************/
1722
1723    /**
1724     * Set the mode options visible.
1725     */
1726    public void showModeOptions() {
1727        /* Make mode options clickable. */
1728        enableModeOptions();
1729        mModeOptionsOverlay.setVisibility(View.VISIBLE);
1730    }
1731
1732    /**
1733     * Set the mode options invisible.  This is necessary for modes
1734     * that don't show a bottom bar for the capture UI.
1735     */
1736    public void hideModeOptions() {
1737        mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1738    }
1739
1740    /****************************Bottom bar api ******************************/
1741
1742    /**
1743     * Sets up the bottom bar and mode options with the correct
1744     * shutter button and visibility based on the current module.
1745     */
1746    public void resetBottomControls(ModuleController module, int moduleIndex) {
1747        if (areBottomControlsUsed(module)) {
1748            setBottomBarShutterIcon(moduleIndex);
1749            mCaptureLayoutHelper.setShowBottomBar(true);
1750        } else {
1751            mCaptureLayoutHelper.setShowBottomBar(false);
1752        }
1753    }
1754
1755    /**
1756     * Show or hide the mode options and bottom bar, based on
1757     * whether the current module is using the bottom bar.  Returns
1758     * whether the mode options and bottom bar are used.
1759     */
1760    private boolean areBottomControlsUsed(ModuleController module) {
1761        if (module.isUsingBottomBar()) {
1762            showBottomBar();
1763            showModeOptions();
1764            return true;
1765        } else {
1766            hideBottomBar();
1767            hideModeOptions();
1768            return false;
1769        }
1770    }
1771
1772    /**
1773     * Set the bottom bar visible.
1774     */
1775    public void showBottomBar() {
1776        mBottomBar.setVisibility(View.VISIBLE);
1777    }
1778
1779    /**
1780     * Set the bottom bar invisible.
1781     */
1782    public void hideBottomBar() {
1783        mBottomBar.setVisibility(View.INVISIBLE);
1784    }
1785
1786    /**
1787     * Sets the color of the bottom bar.
1788     */
1789    public void setBottomBarColor(int colorId) {
1790        mBottomBar.setBackgroundColor(colorId);
1791    }
1792
1793    /**
1794     * Sets the pressed color of the bottom bar for a camera mode index.
1795     */
1796    public void setBottomBarColorsForModeIndex(int index) {
1797        mBottomBar.setColorsForModeIndex(index);
1798    }
1799
1800    /**
1801     * Sets the shutter button icon on the bottom bar, based on
1802     * the mode index.
1803     */
1804    public void setBottomBarShutterIcon(int modeIndex) {
1805        int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1806            mController.getAndroidContext());
1807        mBottomBar.setShutterButtonIcon(shutterIconId);
1808    }
1809
1810    public void animateBottomBarToVideoStop(int shutterIconId) {
1811        mBottomBar.animateToVideoStop(shutterIconId);
1812    }
1813
1814    public void animateBottomBarToFullSize(int shutterIconId) {
1815        mBottomBar.animateToFullSize(shutterIconId);
1816    }
1817
1818    public void setShutterButtonEnabled(final boolean enabled) {
1819        if (!mDisableAllUserInteractions) {
1820            mBottomBar.post(new Runnable() {
1821                @Override
1822                public void run() {
1823                    mBottomBar.setShutterButtonEnabled(enabled);
1824                }
1825            });
1826        }
1827    }
1828
1829    public void setShutterButtonImportantToA11y(boolean important) {
1830        mBottomBar.setShutterButtonImportantToA11y(important);
1831    }
1832
1833    public boolean isShutterButtonEnabled() {
1834        return mBottomBar.isShutterButtonEnabled();
1835    }
1836
1837    public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1838        mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1839    }
1840
1841    /**
1842     * Set the visibility of the bottom bar.
1843     */
1844    // TODO: needed for when panorama is managed by the generic module ui.
1845    public void setBottomBarVisible(boolean visible) {
1846        mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1847    }
1848
1849    /**
1850     * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1851     */
1852    public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1853        mShutterButton.addOnShutterButtonListener(listener);
1854    }
1855
1856    /**
1857     * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1858     */
1859    public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1860        mShutterButton.removeOnShutterButtonListener(listener);
1861    }
1862
1863    /**
1864     * Sets or replaces the "cancel shutter" button listener.
1865     * <p>
1866     * TODO: Make this part of the interface the same way shutter button
1867     * listeners are.
1868     */
1869    public void setCancelShutterButtonListener(View.OnClickListener listener) {
1870        mCountdownCancelButton.setOnClickListener(listener);
1871    }
1872
1873    /**
1874     * Performs a transition to the capture layout of the bottom bar.
1875     */
1876    public void transitionToCapture() {
1877        ModuleController moduleController = mController.getCurrentModuleController();
1878        applyModuleSpecs(moduleController.getHardwareSpec(),
1879            moduleController.getBottomBarSpec());
1880        mBottomBar.transitionToCapture();
1881    }
1882
1883    /**
1884     * Displays the Cancel button instead of the capture button.
1885     */
1886    public void transitionToCancel() {
1887        ModuleController moduleController = mController.getCurrentModuleController();
1888        applyModuleSpecs(moduleController.getHardwareSpec(),
1889                moduleController.getBottomBarSpec());
1890        mBottomBar.transitionToCancel();
1891    }
1892
1893    /**
1894     * Performs a transition to the global intent layout.
1895     */
1896    public void transitionToIntentCaptureLayout() {
1897        ModuleController moduleController = mController.getCurrentModuleController();
1898        applyModuleSpecs(moduleController.getHardwareSpec(),
1899            moduleController.getBottomBarSpec());
1900        mBottomBar.transitionToIntentCaptureLayout();
1901        showModeOptions();
1902    }
1903
1904    /**
1905     * Performs a transition to the global intent review layout.
1906     */
1907    public void transitionToIntentReviewLayout() {
1908        ModuleController moduleController = mController.getCurrentModuleController();
1909        applyModuleSpecs(moduleController.getHardwareSpec(),
1910            moduleController.getBottomBarSpec());
1911        mBottomBar.transitionToIntentReviewLayout();
1912        hideModeOptions();
1913    }
1914
1915    /**
1916     * @return whether UI is in intent review mode
1917     */
1918    public boolean isInIntentReview() {
1919        return mBottomBar.isInIntentReview();
1920    }
1921
1922    @Override
1923    public void onSettingChanged(SettingsManager settingsManager, String key) {
1924        // Update the mode options based on the hardware spec,
1925        // when hdr changes to prevent flash from getting out of sync.
1926        if (key.equals(Keys.KEY_CAMERA_HDR)) {
1927            ModuleController moduleController = mController.getCurrentModuleController();
1928            applyModuleSpecs(moduleController.getHardwareSpec(),
1929                             moduleController.getBottomBarSpec());
1930        }
1931    }
1932
1933    /**
1934     * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1935     * to the bottom bar mode options based on limitations from a
1936     * {@link com.android.camera.hardware.HardwareSpec}.
1937     *
1938     * Options not supported by the hardware are either hidden
1939     * or disabled, depending on the option.
1940     *
1941     * Otherwise, the option is fully enabled and clickable.
1942     */
1943    public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1944           final BottomBarUISpec bottomBarSpec) {
1945        if (hardwareSpec == null || bottomBarSpec == null) {
1946            return;
1947        }
1948
1949        ButtonManager buttonManager = mController.getButtonManager();
1950        SettingsManager settingsManager = mController.getSettingsManager();
1951
1952        buttonManager.setToInitialState();
1953
1954        /** Standard mode options */
1955        if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1956                hardwareSpec.isFrontCameraSupported()) {
1957            if (bottomBarSpec.enableCamera) {
1958                buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1959                        bottomBarSpec.cameraCallback);
1960            } else {
1961                buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1962            }
1963        } else {
1964            // Hide camera icon if front camera not available.
1965            buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1966        }
1967
1968        if (bottomBarSpec.hideFlash || !hardwareSpec.isFlashSupported()) {
1969            // Hide both flash and torch button in flash disable logic
1970            buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1971            buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1972        } else {
1973            if (bottomBarSpec.enableFlash) {
1974                buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1975                    bottomBarSpec.flashCallback);
1976            } else if (bottomBarSpec.enableTorchFlash) {
1977                buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1978                    bottomBarSpec.flashCallback);
1979            } else if (bottomBarSpec.enableHdrPlusFlash) {
1980                buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1981                    bottomBarSpec.flashCallback);
1982            } else {
1983                // Disable both flash and torch button in flash disable logic
1984                buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1985                buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1986            }
1987        }
1988
1989        if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1990            // Force hide hdr or hdr plus icon.
1991            buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1992        } else {
1993            if (hardwareSpec.isHdrPlusSupported()) {
1994                if (bottomBarSpec.enableHdr) {
1995                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1996                            bottomBarSpec.hdrCallback);
1997                } else {
1998                    buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1999                }
2000            } else if (hardwareSpec.isHdrSupported()) {
2001                if (bottomBarSpec.enableHdr) {
2002                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
2003                            bottomBarSpec.hdrCallback);
2004                } else {
2005                    buttonManager.disableButton(ButtonManager.BUTTON_HDR);
2006                }
2007            } else {
2008                // Hide hdr plus or hdr icon if neither are supported.
2009                buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
2010            }
2011        }
2012
2013        if (bottomBarSpec.hideGridLines) {
2014            // Force hide grid lines icon.
2015            buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
2016            hideGridLines();
2017        } else {
2018            if (bottomBarSpec.enableGridLines) {
2019                buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
2020                        bottomBarSpec.gridLinesCallback != null ?
2021                                bottomBarSpec.gridLinesCallback : getGridLinesCallback()
2022                );
2023            } else {
2024                buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
2025                hideGridLines();
2026            }
2027        }
2028
2029        if (bottomBarSpec.enableSelfTimer) {
2030            buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
2031        } else {
2032            if (bottomBarSpec.showSelfTimer) {
2033                buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
2034            } else {
2035                buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
2036            }
2037        }
2038
2039        if (bottomBarSpec.enablePanoOrientation
2040                && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
2041            buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
2042        }
2043
2044        // If manual exposure is enabled and HDR is not enabled, then show the
2045        // exposure button.
2046        // If manual exposure is enabled and HDR is enabled, then disable the
2047        // exposure button.
2048        // If manual exposure is not enabled, then hide the exposure button.
2049        if (bottomBarSpec.enableExposureCompensation
2050                && !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0)
2051                && mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
2052                        Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2053            buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION,
2054                    new View.OnClickListener() {
2055                        @Override
2056                        public void onClick(View v) {
2057                            mModeOptionsOverlay.showExposureOptions();
2058                        }
2059                    });
2060            buttonManager.setExposureCompensationParameters(
2061                bottomBarSpec.minExposureCompensation,
2062                bottomBarSpec.maxExposureCompensation,
2063                bottomBarSpec.exposureCompensationStep);
2064
2065            buttonManager.setExposureCompensationCallback(
2066                    bottomBarSpec.exposureCompensationSetCallback);
2067            buttonManager.updateExposureButtons();
2068        } else if (mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
2069                Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2070            buttonManager.disableButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2071        } else {
2072            buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2073            buttonManager.setExposureCompensationCallback(null);
2074        }
2075
2076        /** Intent UI */
2077        if (bottomBarSpec.showCancel) {
2078            buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2079                    bottomBarSpec.cancelCallback);
2080        }
2081        if (bottomBarSpec.showDone) {
2082            buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2083                    bottomBarSpec.doneCallback);
2084        }
2085        if (bottomBarSpec.showRetake) {
2086            buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2087                    bottomBarSpec.retakeCallback,
2088                    R.drawable.ic_back,
2089                    R.string.retake_button_description);
2090        }
2091        if (bottomBarSpec.showReview) {
2092            buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2093                    bottomBarSpec.reviewCallback,
2094                    R.drawable.ic_play,
2095                    R.string.review_button_description);
2096        }
2097    }
2098
2099    /**
2100     * Shows the given tutorial on the screen.
2101     */
2102    public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2103        tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2104    }
2105
2106    /***************************Filmstrip api *****************************/
2107
2108    public void showFilmstrip() {
2109        mModeListView.onBackPressed();
2110        mFilmstripLayout.showFilmstrip();
2111    }
2112
2113    public void hideFilmstrip() {
2114        mFilmstripLayout.hideFilmstrip();
2115    }
2116
2117    public int getFilmstripVisibility() {
2118        return mFilmstripLayout.getVisibility();
2119    }
2120}
2121