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.BottomBarModeOptionsWrapper;
53import com.android.camera.ui.CaptureAnimationOverlay;
54import com.android.camera.ui.GridLines;
55import com.android.camera.ui.MainActivityLayout;
56import com.android.camera.ui.ModeListView;
57import com.android.camera.ui.ModeTransitionView;
58import com.android.camera.ui.PreviewOverlay;
59import com.android.camera.ui.PreviewStatusListener;
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.PeekView;
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 BottomBarModeOptionsWrapper mIndicatorBottomBarWrapper;
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 PeekView mPeekView;
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        mPeekView = (PeekView) appRootView.findViewById(R.id.peek_view);
790        mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
791        initDisplayListener();
792        mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
793        View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
794        modeListToggle.setOnClickListener(new View.OnClickListener() {
795            @Override
796            public void onClick(View view) {
797                openModeList();
798            }
799        });
800        View filmstripToggle = mAppRootView.findViewById(
801                R.id.accessibility_filmstrip_toggle_button);
802        filmstripToggle.setOnClickListener(new View.OnClickListener() {
803            @Override
804            public void onClick(View view) {
805                showFilmstrip();
806            }
807        });
808    }
809
810
811    /**
812     * Freeze what is currently shown on screen until the next preview frame comes
813     * in.
814     */
815    public void freezeScreenUntilPreviewReady() {
816        Log.v(TAG, "freezeScreenUntilPreviewReady");
817        mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
818                .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
819        mHideCoverRunnable = new Runnable() {
820            @Override
821            public void run() {
822                mModeTransitionView.hideImageCover();
823            }
824        };
825        mModeCoverState = COVER_SHOWN;
826    }
827
828    /**
829     * Creates a cling for the specific viewer and links the cling to the corresponding
830     * button for layout position.
831     *
832     * @param viewerType defines which viewer the cling is for.
833     */
834    public void setupClingForViewer(int viewerType) {
835        if (viewerType == BottomPanel.VIEWER_REFOCUS) {
836            FrameLayout filmstripContent = (FrameLayout) mAppRootView
837                    .findViewById(R.id.camera_filmstrip_content_layout);
838            if (filmstripContent != null) {
839                // Creates refocus cling.
840                LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
841                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
842                Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
843                // Sets instruction text in the cling.
844                refocusCling.setText(mController.getAndroidContext().getResources()
845                        .getString(R.string.cling_text_for_refocus_editor_button));
846
847                // Adds cling into view hierarchy.
848                int clingWidth = mController.getAndroidContext()
849                        .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
850                filmstripContent.addView(refocusCling, clingWidth,
851                        ViewGroup.LayoutParams.WRAP_CONTENT);
852                mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
853            }
854        }
855    }
856
857    /**
858     * Clears the listeners for the cling and remove it from the view hierarchy.
859     *
860     * @param viewerType defines which viewer the cling is for.
861     */
862    public void clearClingForViewer(int viewerType) {
863        Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
864        if (clingToBeRemoved == null) {
865            // No cling is created for the specific viewer type.
866            return;
867        }
868        mFilmstripBottomControls.clearClingForViewer(viewerType);
869        clingToBeRemoved.setVisibility(View.GONE);
870        mAppRootView.removeView(clingToBeRemoved);
871    }
872
873    /**
874     * Enable or disable swipe gestures. We want to disable them e.g. while we
875     * record a video.
876     */
877    public void setSwipeEnabled(boolean enabled) {
878        mSwipeEnabled = enabled;
879        // TODO: This can be removed once we come up with a new design for handling swipe
880        // on shutter button and mode options. (More details: b/13751653)
881        mAppRootView.setSwipeEnabled(enabled);
882    }
883
884    public void onDestroy() {
885        ((DisplayManager) mController.getAndroidContext()
886                .getSystemService(Context.DISPLAY_SERVICE))
887                .unregisterDisplayListener(mDisplayListener);
888    }
889
890    /**
891     * Initializes the display listener to listen to display changes such as
892     * 180-degree rotation change, which will not have an onConfigurationChanged
893     * callback.
894     */
895    private void initDisplayListener() {
896        if (ApiHelper.HAS_DISPLAY_LISTENER) {
897            mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext());
898
899            mDisplayListener = new DisplayManager.DisplayListener() {
900                @Override
901                public void onDisplayAdded(int arg0) {
902                    // Do nothing.
903                }
904
905                @Override
906                public void onDisplayChanged(int displayId) {
907                    int rotation = CameraUtil.getDisplayRotation(
908                            mController.getAndroidContext());
909                    if ((rotation - mLastRotation + 360) % 360 == 180
910                            && mPreviewStatusListener != null) {
911                        mPreviewStatusListener.onPreviewFlipped();
912                        mIndicatorBottomBarWrapper.requestLayout();
913                        mModeListView.requestLayout();
914                        mTextureView.requestLayout();
915                    }
916                    mLastRotation = rotation;
917                }
918
919                @Override
920                public void onDisplayRemoved(int arg0) {
921                    // Do nothing.
922                }
923            };
924
925            ((DisplayManager) mController.getAndroidContext()
926                    .getSystemService(Context.DISPLAY_SERVICE))
927                    .registerDisplayListener(mDisplayListener, null);
928        }
929    }
930
931    /**
932     * Redirects touch events to appropriate recipient views based on swipe direction.
933     * More specifically, swipe up and swipe down will be handled by the view that handles
934     * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
935     * to mode list in order to bring up mode list.
936     */
937    private void onSwipeDetected(int swipeState) {
938        if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
939            // TODO: Polish quick switch after this release.
940            // Quick switch between modes.
941            int currentModuleIndex = mController.getCurrentModuleIndex();
942            final int moduleToTransitionTo =
943                    mController.getQuickSwitchToModuleId(currentModuleIndex);
944            if (currentModuleIndex != moduleToTransitionTo) {
945                mAppRootView.redirectTouchEventsTo(mModeTransitionView);
946                int shadeColorId = R.color.mode_cover_default_color;
947                int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
948                        mController.getAndroidContext());
949
950                AnimationFinishedListener listener = new AnimationFinishedListener() {
951                    @Override
952                    public void onAnimationFinished(boolean success) {
953                        if (success) {
954                            mHideCoverRunnable = new Runnable() {
955                                @Override
956                                public void run() {
957                                    mModeTransitionView.startPeepHoleAnimation();
958                                }
959                            };
960                            mModeCoverState = COVER_SHOWN;
961                            // Go to new module when the previous operation is successful.
962                            mController.onModeSelected(moduleToTransitionTo);
963                        }
964                    }
965                };
966            }
967        } else if (swipeState == SWIPE_LEFT) {
968            // Pass the touch sequence to filmstrip layout.
969            mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
970        } else if (swipeState == SWIPE_RIGHT) {
971            // Pass the touch to mode switcher
972            mAppRootView.redirectTouchEventsTo(mModeListView);
973        }
974    }
975
976    /**
977     * Gets called when activity resumes in preview.
978     */
979    public void resume() {
980        // Show mode theme cover until preview is ready
981        showModeCoverUntilPreviewReady();
982
983        // Hide action bar first since we are in full screen mode first, and
984        // switch the system UI to lights-out mode.
985        mFilmstripPanel.hide();
986
987        // Show UI that is meant to only be used when spoken feedback is
988        // enabled.
989        mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
990        mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
991    }
992
993    /**
994     * @return Whether any spoken feedback accessibility feature is currently
995     *         enabled.
996     */
997    private boolean isSpokenFeedbackAccessibilityEnabled() {
998        AccessibilityManager accessibilityManager = (AccessibilityManager) mController
999                .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1000        List<AccessibilityServiceInfo> infos = accessibilityManager
1001                .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1002        return infos != null && !infos.isEmpty();
1003    }
1004
1005    /**
1006     * Opens the mode list (e.g. because of the menu button being pressed) and
1007     * adapts the rest of the UI.
1008     */
1009    public void openModeList() {
1010        mModeOptionsOverlay.closeModeOptions();
1011        mModeListView.onMenuPressed();
1012    }
1013
1014    /**
1015     * A cover view showing the mode theme color and mode icon will be visible on
1016     * top of preview until preview is ready (i.e. camera preview is started and
1017     * the first frame has been received).
1018     */
1019    private void showModeCoverUntilPreviewReady() {
1020        int modeId = mController.getCurrentModuleIndex();
1021        int colorId = R.color.mode_cover_default_color;;
1022        int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1023        mModeTransitionView.setupModeCover(colorId, iconId);
1024        mHideCoverRunnable = new Runnable() {
1025            @Override
1026            public void run() {
1027                mModeTransitionView.hideModeCover(null);
1028                if (!mDisableAllUserInteractions) {
1029                    showShimmyDelayed();
1030                }
1031            }
1032        };
1033        mModeCoverState = COVER_SHOWN;
1034    }
1035
1036    private void showShimmyDelayed() {
1037        if (!mIsCaptureIntent) {
1038            // Show shimmy in SHIMMY_DELAY_MS
1039            mModeListView.showModeSwitcherHint();
1040        }
1041    }
1042
1043    private void hideModeCover() {
1044        if (mHideCoverRunnable != null) {
1045            mAppRootView.post(mHideCoverRunnable);
1046            mHideCoverRunnable = null;
1047        }
1048        mModeCoverState = COVER_HIDDEN;
1049        if (mCoverHiddenTime < 0) {
1050            mCoverHiddenTime = System.currentTimeMillis();
1051        }
1052    }
1053
1054
1055    public void onPreviewVisiblityChanged(int visibility) {
1056        if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1057            setIndicatorBottomBarWrapperVisible(false);
1058            mAccessibilityAffordances.setVisibility(View.GONE);
1059        } else {
1060            setIndicatorBottomBarWrapperVisible(true);
1061            if (mAccessibilityEnabled) {
1062                mAccessibilityAffordances.setVisibility(View.VISIBLE);
1063            } else {
1064                mAccessibilityAffordances.setVisibility(View.GONE);
1065            }
1066        }
1067    }
1068
1069    /**
1070     * Call to stop the preview from being rendered.
1071     */
1072    public void pausePreviewRendering() {
1073        mTextureView.setVisibility(View.INVISIBLE);
1074    }
1075
1076    /**
1077     * Call to begin rendering the preview again.
1078     */
1079    public void resumePreviewRendering() {
1080        mTextureView.setVisibility(View.VISIBLE);
1081    }
1082
1083    /**
1084     * Returns the transform associated with the preview view.
1085     *
1086     * @param m the Matrix in which to copy the current transform.
1087     * @return The specified matrix if not null or a new Matrix instance
1088     *         otherwise.
1089     */
1090    public Matrix getPreviewTransform(Matrix m) {
1091        return mTextureView.getTransform(m);
1092    }
1093
1094    @Override
1095    public void onOpenFullScreen() {
1096        // Do nothing.
1097    }
1098
1099    @Override
1100    public void onModeListOpenProgress(float progress) {
1101        progress = 1 - progress;
1102        float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1103        mModeOptionsToggle.setAlpha(interpolatedProgress);
1104        // Change shutter button alpha linearly based on the mode list open progress:
1105        // set the alpha to disabled alpha when list is fully open, to enabled alpha
1106        // when the list is fully closed.
1107        mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1108                + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1109    }
1110
1111    @Override
1112    public void onModeListClosed() {
1113        // Make sure the alpha on mode options ellipse is reset when mode drawer
1114        // is closed.
1115        mModeOptionsToggle.setAlpha(1f);
1116        mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1117    }
1118
1119    /**
1120     * Called when the back key is pressed.
1121     *
1122     * @return Whether the UI responded to the key event.
1123     */
1124    public boolean onBackPressed() {
1125        if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1126            return mFilmstripLayout.onBackPressed();
1127        } else {
1128            return mModeListView.onBackPressed();
1129        }
1130    }
1131
1132    /**
1133     * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1134     * listens to SurfaceTexture changes. In addition, listeners are set on
1135     * dependent app ui elements.
1136     *
1137     * @param previewStatusListener the listener that gets notified when SurfaceTexture
1138     *                              changes
1139     */
1140    public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1141        mPreviewStatusListener = previewStatusListener;
1142        if (mPreviewStatusListener != null) {
1143            onPreviewListenerChanged();
1144        }
1145    }
1146
1147    /**
1148     * When the PreviewStatusListener changes, listeners need to be
1149     * set on the following app ui elements:
1150     * {@link com.android.camera.ui.PreviewOverlay},
1151     * {@link com.android.camera.ui.BottomBar},
1152     * {@link com.android.camera.ui.IndicatorIconController}.
1153     */
1154    private void onPreviewListenerChanged() {
1155        // Set a listener for recognizing preview gestures.
1156        GestureDetector.OnGestureListener gestureListener
1157            = mPreviewStatusListener.getGestureListener();
1158        if (gestureListener != null) {
1159            mPreviewOverlay.setGestureListener(gestureListener);
1160        }
1161        View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1162        if (touchListener != null) {
1163            mPreviewOverlay.setTouchListener(touchListener);
1164        }
1165
1166        mTextureViewHelper.setAutoAdjustTransform(
1167                mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1168    }
1169
1170    /**
1171     * This method should be called in onCameraOpened.  It defines CameraAppUI
1172     * specific changes that depend on the camera or camera settings.
1173     */
1174    public void onChangeCamera() {
1175        ModuleController moduleController = mController.getCurrentModuleController();
1176        applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1177
1178        if (mIndicatorIconController != null) {
1179            // Sync the settings state with the indicator state.
1180            mIndicatorIconController.syncIndicators();
1181        }
1182    }
1183
1184    /**
1185     * Adds a listener to receive callbacks when preview area changes.
1186     */
1187    public void addPreviewAreaChangedListener(
1188            PreviewStatusListener.PreviewAreaChangedListener listener) {
1189        mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1190    }
1191
1192    /**
1193     * Removes a listener that receives callbacks when preview area changes.
1194     */
1195    public void removePreviewAreaChangedListener(
1196            PreviewStatusListener.PreviewAreaChangedListener listener) {
1197        mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1198    }
1199
1200    /**
1201     * This inflates generic_module layout, which contains all the shared views across
1202     * modules. Then each module inflates their own views in the given view group. For
1203     * now, this is called every time switching from a not-yet-refactored module to a
1204     * refactored module. In the future, this should only need to be done once per app
1205     * start.
1206     */
1207    public void prepareModuleUI() {
1208        mController.getSettingsManager().addListener(this);
1209        mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1210        mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1211        mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1212                mController.getCameraProvider());
1213        mTextureViewHelper.setSurfaceTextureListener(this);
1214        mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1215
1216        mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1217        int unpressedColor = mController.getAndroidContext().getResources()
1218            .getColor(R.color.bottombar_unpressed);
1219        setBottomBarColor(unpressedColor);
1220        updateModeSpecificUIColors();
1221
1222        mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1223
1224        mModeOptionsOverlay
1225            = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1226
1227        // Sets the visibility of the bottom bar and the mode options.
1228        resetBottomControls(mController.getCurrentModuleController(),
1229            mController.getCurrentModuleIndex());
1230        mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1231
1232        mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1233        addShutterListener(mController.getCurrentModuleController());
1234        addShutterListener(mModeOptionsOverlay);
1235        addShutterListener(this);
1236
1237        mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1238        mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1239
1240        mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1241        mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1242        mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1243
1244        mCaptureOverlay = (CaptureAnimationOverlay)
1245                mCameraRootView.findViewById(R.id.capture_overlay);
1246        mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1247        mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1248
1249        if (mIndicatorIconController == null) {
1250            mIndicatorIconController =
1251                new IndicatorIconController(mController, mAppRootView);
1252        }
1253
1254        mController.getButtonManager().load(mCameraRootView);
1255        mController.getButtonManager().setListener(mIndicatorIconController);
1256        mController.getSettingsManager().addListener(mIndicatorIconController);
1257
1258        mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1259        mFocusOverlay = mCameraRootView.findViewById(R.id.focus_overlay);
1260        mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1261                .findViewById(R.id.tutorials_placeholder_wrapper);
1262        mIndicatorBottomBarWrapper = (BottomBarModeOptionsWrapper) mAppRootView
1263                .findViewById(R.id.indicator_bottombar_wrapper);
1264        mIndicatorBottomBarWrapper.setCaptureLayoutHelper(mCaptureLayoutHelper);
1265        mTextureViewHelper.addPreviewAreaSizeChangedListener(
1266                new PreviewStatusListener.PreviewAreaChangedListener() {
1267                    @Override
1268                    public void onPreviewAreaChanged(RectF previewArea) {
1269                        mPeekView.setTranslationX(previewArea.right - mAppRootView.getRight());
1270                    }
1271                });
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    /**
1376     * Set the mode options toggle clickable.
1377     */
1378    public void enableModeOptions() {
1379        /*
1380         * For modules using camera 1 api, this gets called in
1381         * onSurfaceTextureUpdated whenever the preview gets stopped and
1382         * started after each capture.  This also takes care of the
1383         * case where the mode options might be unclickable when we
1384         * switch modes
1385         *
1386         * For modules using camera 2 api, they're required to call this
1387         * method when a capture is "completed".  Unfortunately this differs
1388         * per module implementation.
1389         */
1390        if (!mDisableAllUserInteractions) {
1391            mModeOptionsOverlay.setToggleClickable(true);
1392        }
1393    }
1394
1395    /**
1396     * Set the mode options toggle not clickable.
1397     */
1398    public void disableModeOptions() {
1399        mModeOptionsOverlay.setToggleClickable(false);
1400    }
1401
1402    public void setDisableAllUserInteractions(boolean disable) {
1403        if (disable) {
1404            disableModeOptions();
1405            setShutterButtonEnabled(false);
1406            setSwipeEnabled(false);
1407            mModeListView.hideAnimated();
1408        } else {
1409            enableModeOptions();
1410            setShutterButtonEnabled(true);
1411            setSwipeEnabled(true);
1412        }
1413        mDisableAllUserInteractions = disable;
1414    }
1415
1416    /**
1417     * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1418     *
1419     * @param modeIndex mode index of the selected mode
1420     */
1421    @Override
1422    public void onModeSelected(int modeIndex) {
1423        mHideCoverRunnable = new Runnable() {
1424            @Override
1425            public void run() {
1426                mModeListView.startModeSelectionAnimation();
1427            }
1428        };
1429        mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1430        mModeCoverState = COVER_SHOWN;
1431
1432        int lastIndex = mController.getCurrentModuleIndex();
1433        // Actual mode teardown / new mode initialization happens here
1434        mController.onModeSelected(modeIndex);
1435        int currentIndex = mController.getCurrentModuleIndex();
1436
1437        if (lastIndex == currentIndex) {
1438            hideModeCover();
1439        }
1440
1441        updateModeSpecificUIColors();
1442    }
1443
1444    private void updateModeSpecificUIColors() {
1445        setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1446    }
1447
1448    @Override
1449    public void onSettingsSelected() {
1450        mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1451                                             Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1452        mModeListView.setShouldShowSettingsCling(false);
1453        mController.onSettingsSelected();
1454    }
1455
1456    @Override
1457    public int getCurrentModeIndex() {
1458        return mController.getCurrentModuleIndex();
1459    }
1460
1461    /********************** Capture animation **********************/
1462    /* TODO: This session is subject to UX changes. In addition to the generic
1463       flash animation and post capture animation, consider designating a parameter
1464       for specifying the type of animation, as well as an animation finished listener
1465       so that modules can have more knowledge of the status of the animation. */
1466
1467    /**
1468     * Starts the filmstrip peek animation.
1469     *
1470     * @param bitmap The bitmap to show.
1471     * @param strong Whether the animation shows more portion of the bitmap or
1472     *               not.
1473     * @param accessibilityString An accessibility String to be announced
1474                     during the peek animation.
1475     */
1476    public void startPeekAnimation(Bitmap bitmap, boolean strong, String accessibilityString) {
1477        if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1478            return;
1479        }
1480        mPeekView.startPeekAnimation(bitmap, strong, accessibilityString);
1481    }
1482
1483    /**
1484     * Starts the pre-capture animation.
1485     *
1486     * @param shortFlash show shortest possible flash instead of regular long version.
1487     */
1488    public void startPreCaptureAnimation(boolean shortFlash) {
1489        mCaptureOverlay.startFlashAnimation(shortFlash);
1490    }
1491
1492    /**
1493     * Cancels the pre-capture animation.
1494     */
1495    public void cancelPreCaptureAnimation() {
1496        mAnimationManager.cancelAnimations();
1497    }
1498
1499    /**
1500     * Cancels the post-capture animation.
1501     */
1502    public void cancelPostCaptureAnimation() {
1503        mAnimationManager.cancelAnimations();
1504    }
1505
1506    public FilmstripContentPanel getFilmstripContentPanel() {
1507        return mFilmstripPanel;
1508    }
1509
1510    /**
1511     * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1512     * bottom of the filmstrip.
1513     */
1514    public BottomPanel getFilmstripBottomControls() {
1515        return mFilmstripBottomControls;
1516    }
1517
1518    public void showBottomControls() {
1519        mFilmstripBottomControls.show();
1520    }
1521
1522    public void hideBottomControls() {
1523        mFilmstripBottomControls.hide();
1524    }
1525
1526    /**
1527     * @param listener The listener for bottom controls.
1528     */
1529    public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1530        mFilmstripBottomControls.setListener(listener);
1531    }
1532
1533    /***************************SurfaceTexture Api and Listener*********************************/
1534
1535    /**
1536     * Return the shared surface texture.
1537     */
1538    public SurfaceTexture getSurfaceTexture() {
1539        return mSurface;
1540    }
1541
1542    /**
1543     * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1544     */
1545    public int getSurfaceWidth() {
1546        return mSurfaceWidth;
1547    }
1548
1549    /**
1550     * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1551     */
1552    public int getSurfaceHeight() {
1553        return mSurfaceHeight;
1554    }
1555
1556    @Override
1557    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1558        mSurface = surface;
1559        mSurfaceWidth = width;
1560        mSurfaceHeight = height;
1561        Log.v(TAG, "SurfaceTexture is available");
1562        if (mPreviewStatusListener != null) {
1563            mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1564        }
1565        enableModeOptions();
1566    }
1567
1568    @Override
1569    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1570        mSurface = surface;
1571        mSurfaceWidth = width;
1572        mSurfaceHeight = height;
1573        if (mPreviewStatusListener != null) {
1574            mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1575        }
1576    }
1577
1578    @Override
1579    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1580        mSurface = null;
1581        Log.v(TAG, "SurfaceTexture is destroyed");
1582        if (mPreviewStatusListener != null) {
1583            return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1584        }
1585        return false;
1586    }
1587
1588    @Override
1589    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1590        mSurface = surface;
1591        if (mPreviewStatusListener != null) {
1592            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1593        }
1594        if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1595            Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1596            CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1597            hideModeCover();
1598        }
1599    }
1600
1601    /****************************Grid lines api ******************************/
1602
1603    /**
1604     * Show a set of evenly spaced lines over the preview.  The number
1605     * of lines horizontally and vertically is determined by
1606     * {@link com.android.camera.ui.GridLines}.
1607     */
1608    public void showGridLines() {
1609        if (mGridLines != null) {
1610            mGridLines.setVisibility(View.VISIBLE);
1611        }
1612    }
1613
1614    /**
1615     * Hide the set of evenly spaced grid lines overlaying the preview.
1616     */
1617    public void hideGridLines() {
1618        if (mGridLines != null) {
1619            mGridLines.setVisibility(View.INVISIBLE);
1620        }
1621    }
1622
1623    /**
1624     * Return a callback which shows or hide the preview grid lines
1625     * depending on whether the grid lines setting is set on.
1626     */
1627    public ButtonManager.ButtonCallback getGridLinesCallback() {
1628        return new ButtonManager.ButtonCallback() {
1629            @Override
1630            public void onStateChanged(int state) {
1631                if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1632                    showGridLines();
1633                } else {
1634                    hideGridLines();
1635                }
1636            }
1637        };
1638    }
1639
1640    /***************************Mode options api *****************************/
1641
1642    /**
1643     * Set the mode options visible.
1644     */
1645    public void showModeOptions() {
1646        /* Make mode options clickable. */
1647        enableModeOptions();
1648        mModeOptionsOverlay.setVisibility(View.VISIBLE);
1649    }
1650
1651    /**
1652     * Set the mode options invisible.  This is necessary for modes
1653     * that don't show a bottom bar for the capture UI.
1654     */
1655    public void hideModeOptions() {
1656        mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1657    }
1658
1659    /****************************Bottom bar api ******************************/
1660
1661    /**
1662     * Sets up the bottom bar and mode options with the correct
1663     * shutter button and visibility based on the current module.
1664     */
1665    public void resetBottomControls(ModuleController module, int moduleIndex) {
1666        if (areBottomControlsUsed(module)) {
1667            setBottomBarShutterIcon(moduleIndex);
1668            mCaptureLayoutHelper.setShowBottomBar(true);
1669        } else {
1670            mCaptureLayoutHelper.setShowBottomBar(false);
1671        }
1672    }
1673
1674    /**
1675     * Show or hide the mode options and bottom bar, based on
1676     * whether the current module is using the bottom bar.  Returns
1677     * whether the mode options and bottom bar are used.
1678     */
1679    private boolean areBottomControlsUsed(ModuleController module) {
1680        if (module.isUsingBottomBar()) {
1681            showBottomBar();
1682            showModeOptions();
1683            return true;
1684        } else {
1685            hideBottomBar();
1686            hideModeOptions();
1687            return false;
1688        }
1689    }
1690
1691    /**
1692     * Set the bottom bar visible.
1693     */
1694    public void showBottomBar() {
1695        mBottomBar.setVisibility(View.VISIBLE);
1696    }
1697
1698    /**
1699     * Set the bottom bar invisible.
1700     */
1701    public void hideBottomBar() {
1702        mBottomBar.setVisibility(View.INVISIBLE);
1703    }
1704
1705    /**
1706     * Sets the color of the bottom bar.
1707     */
1708    public void setBottomBarColor(int colorId) {
1709        mBottomBar.setBackgroundColor(colorId);
1710    }
1711
1712    /**
1713     * Sets the pressed color of the bottom bar for a camera mode index.
1714     */
1715    public void setBottomBarColorsForModeIndex(int index) {
1716        mBottomBar.setColorsForModeIndex(index);
1717    }
1718
1719    /**
1720     * Sets the shutter button icon on the bottom bar, based on
1721     * the mode index.
1722     */
1723    public void setBottomBarShutterIcon(int modeIndex) {
1724        int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1725            mController.getAndroidContext());
1726        mBottomBar.setShutterButtonIcon(shutterIconId);
1727    }
1728
1729    public void animateBottomBarToVideoStop(int shutterIconId) {
1730        mBottomBar.animateToVideoStop(shutterIconId);
1731    }
1732
1733    public void animateBottomBarToFullSize(int shutterIconId) {
1734        mBottomBar.animateToFullSize(shutterIconId);
1735    }
1736
1737    public void setShutterButtonEnabled(final boolean enabled) {
1738        if (!mDisableAllUserInteractions) {
1739            mBottomBar.post(new Runnable() {
1740                @Override
1741                public void run() {
1742                    mBottomBar.setShutterButtonEnabled(enabled);
1743                }
1744            });
1745        }
1746    }
1747
1748    public void setShutterButtonImportantToA11y(boolean important) {
1749        mBottomBar.setShutterButtonImportantToA11y(important);
1750    }
1751
1752    public boolean isShutterButtonEnabled() {
1753        return mBottomBar.isShutterButtonEnabled();
1754    }
1755
1756    public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1757        mIndicatorBottomBarWrapper.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1758    }
1759
1760    /**
1761     * Set the visibility of the bottom bar.
1762     */
1763    // TODO: needed for when panorama is managed by the generic module ui.
1764    public void setBottomBarVisible(boolean visible) {
1765        mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1766    }
1767
1768    /**
1769     * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1770     */
1771    public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1772        mShutterButton.addOnShutterButtonListener(listener);
1773    }
1774
1775    /**
1776     * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1777     */
1778    public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1779        mShutterButton.removeOnShutterButtonListener(listener);
1780    }
1781
1782    /**
1783     * Performs a transition to the capture layout of the bottom bar.
1784     */
1785    public void transitionToCapture() {
1786        ModuleController moduleController = mController.getCurrentModuleController();
1787        applyModuleSpecs(moduleController.getHardwareSpec(),
1788            moduleController.getBottomBarSpec());
1789        mBottomBar.transitionToCapture();
1790    }
1791
1792    /**
1793     * Displays the Cancel button instead of the capture button.
1794     */
1795    public void transitionToCancel() {
1796        ModuleController moduleController = mController.getCurrentModuleController();
1797        applyModuleSpecs(moduleController.getHardwareSpec(),
1798                moduleController.getBottomBarSpec());
1799        mBottomBar.transitionToCancel();
1800    }
1801
1802    /**
1803     * Performs a transition to the global intent layout.
1804     */
1805    public void transitionToIntentCaptureLayout() {
1806        ModuleController moduleController = mController.getCurrentModuleController();
1807        applyModuleSpecs(moduleController.getHardwareSpec(),
1808            moduleController.getBottomBarSpec());
1809        mBottomBar.transitionToIntentCaptureLayout();
1810    }
1811
1812    /**
1813     * Performs a transition to the global intent review layout.
1814     */
1815    public void transitionToIntentReviewLayout() {
1816        ModuleController moduleController = mController.getCurrentModuleController();
1817        applyModuleSpecs(moduleController.getHardwareSpec(),
1818            moduleController.getBottomBarSpec());
1819        mBottomBar.transitionToIntentReviewLayout();
1820    }
1821
1822    /**
1823     * @return whether UI is in intent review mode
1824     */
1825    public boolean isInIntentReview() {
1826        return mBottomBar.isInIntentReview();
1827    }
1828
1829    @Override
1830    public void onSettingChanged(SettingsManager settingsManager, String key) {
1831        // Update the mode options based on the hardware spec,
1832        // when hdr changes to prevent flash from getting out of sync.
1833        if (key.equals(Keys.KEY_CAMERA_HDR)) {
1834            ModuleController moduleController = mController.getCurrentModuleController();
1835            applyModuleSpecs(moduleController.getHardwareSpec(),
1836                             moduleController.getBottomBarSpec());
1837        }
1838    }
1839
1840    /**
1841     * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1842     * to the bottom bar mode options based on limitations from a
1843     * {@link com.android.camera.hardware.HardwareSpec}.
1844     *
1845     * Options not supported by the hardware are either hidden
1846     * or disabled, depending on the option.
1847     *
1848     * Otherwise, the option is fully enabled and clickable.
1849     */
1850    public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1851           final BottomBarUISpec bottomBarSpec) {
1852        if (hardwareSpec == null || bottomBarSpec == null) {
1853            return;
1854        }
1855
1856        ButtonManager buttonManager = mController.getButtonManager();
1857        SettingsManager settingsManager = mController.getSettingsManager();
1858
1859        buttonManager.setToInitialState();
1860
1861        /** Standard mode options */
1862        if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1863                hardwareSpec.isFrontCameraSupported()) {
1864            if (bottomBarSpec.enableCamera) {
1865                buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1866                        bottomBarSpec.cameraCallback);
1867            } else {
1868                buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1869            }
1870        } else {
1871            // Hide camera icon if front camera not available.
1872            buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1873        }
1874
1875        boolean flashBackCamera = mController.getSettingsManager().getBoolean(
1876            SettingsManager.SCOPE_GLOBAL, Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA);
1877        if (bottomBarSpec.hideFlash || !flashBackCamera) {
1878            // Hide both flash and torch button in flash disable logic
1879            buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1880            buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1881        } else {
1882            if (hardwareSpec.isFlashSupported()) {
1883                if (bottomBarSpec.enableFlash) {
1884                    buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1885                        bottomBarSpec.flashCallback);
1886                } else if (bottomBarSpec.enableTorchFlash) {
1887                    buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1888                        bottomBarSpec.flashCallback);
1889                } else if (bottomBarSpec.enableHdrPlusFlash) {
1890                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1891                        bottomBarSpec.flashCallback);
1892                } else {
1893                    // Hide both flash and torch button in flash disable logic
1894                    buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1895                    buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1896                }
1897            } else {
1898                // Disable both flash and torch icon if not supported
1899                // by the chosen camera hardware.
1900                buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1901                buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1902            }
1903        }
1904
1905        if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1906            // Force hide hdr or hdr plus icon.
1907            buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1908        } else {
1909            if (hardwareSpec.isHdrPlusSupported()) {
1910                if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1911                                                                       mController.getModuleScope())) {
1912                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1913                            bottomBarSpec.hdrCallback);
1914                } else {
1915                    buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1916                }
1917            } else if (hardwareSpec.isHdrSupported()) {
1918                if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1919                                                                       mController.getModuleScope())) {
1920                    buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1921                            bottomBarSpec.hdrCallback);
1922                } else {
1923                    buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1924                }
1925            } else {
1926                // Hide hdr plus or hdr icon if neither are supported.
1927                buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1928            }
1929        }
1930
1931        if (bottomBarSpec.hideGridLines) {
1932            // Force hide grid lines icon.
1933            buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1934            hideGridLines();
1935        } else {
1936            if (bottomBarSpec.enableGridLines) {
1937                buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1938                        bottomBarSpec.gridLinesCallback != null ?
1939                                bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1940                );
1941            } else {
1942                buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1943                hideGridLines();
1944            }
1945        }
1946
1947        if (bottomBarSpec.enableSelfTimer) {
1948            buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
1949        } else {
1950            if (bottomBarSpec.showSelfTimer) {
1951                buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
1952            } else {
1953                buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
1954            }
1955        }
1956
1957        if (bottomBarSpec.enablePanoOrientation
1958                && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
1959            buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
1960        }
1961
1962        boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
1963            !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
1964            mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
1965                        Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
1966        if (enableExposureCompensation) {
1967            buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
1968            buttonManager.setExposureCompensationParameters(
1969                bottomBarSpec.minExposureCompensation,
1970                bottomBarSpec.maxExposureCompensation,
1971                bottomBarSpec.exposureCompensationStep);
1972
1973            buttonManager.setExposureCompensationCallback(
1974                    bottomBarSpec.exposureCompensationSetCallback);
1975            buttonManager.updateExposureButtons();
1976        } else {
1977            buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
1978            buttonManager.setExposureCompensationCallback(null);
1979        }
1980
1981        /** Intent UI */
1982        if (bottomBarSpec.showCancel) {
1983            buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
1984                    bottomBarSpec.cancelCallback);
1985        }
1986        if (bottomBarSpec.showDone) {
1987            buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
1988                    bottomBarSpec.doneCallback);
1989        }
1990        if (bottomBarSpec.showRetake) {
1991            buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
1992                    bottomBarSpec.retakeCallback);
1993        }
1994        if (bottomBarSpec.showReview) {
1995            buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
1996                    bottomBarSpec.reviewCallback,
1997                    R.drawable.ic_play);
1998        }
1999    }
2000
2001    /**
2002     * Shows the given tutorial on the screen.
2003     */
2004    public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2005        tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2006    }
2007
2008    /***************************Filmstrip api *****************************/
2009
2010    public void showFilmstrip() {
2011        mModeListView.onBackPressed();
2012        mFilmstripLayout.showFilmstrip();
2013    }
2014
2015    public void hideFilmstrip() {
2016        mFilmstripLayout.hideFilmstrip();
2017    }
2018}
2019