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