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